Skip to content

Commit 6ff8a35

Browse files
authored
Merge pull request #487 from CodeForAfrica/share_component
feat(act-now): implement Act Now card components and integrate site settings
2 parents 17378b9 + 6fa1ef4 commit 6ff8a35

File tree

12 files changed

+718
-12
lines changed

12 files changed

+718
-12
lines changed

src/app/(frontend)/[entitySlug]/promises/[promiseId]/page.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@ import React from "react";
22
import NextLink from "next/link";
33
import Image from "next/image";
44
import type { Metadata } from "next";
5-
import { notFound, redirect } from "next/navigation";
6-
import { Box, Button, Container, Grid, Typography } from "@mui/material";
5+
import { notFound } from "next/navigation";
6+
import { Box, Container, Grid, Typography } from "@mui/material";
77

88
import Navigation from "@/components/Navigation";
99
import Footer from "@/components/Footer";
1010
import PromiseStatus from "@/components/PromiseStatus";
1111
import PromiseTimeline from "@/components/PromiseTimeline";
12+
import ActNowCard from "@/components/ActNowCard";
1213
import { CommonHomePage } from "@/components/CommonHomePage";
1314
import { getDomain } from "@/lib/domain";
14-
import { getTenantBySubDomain, getTenantNavigation } from "@/lib/data/tenants";
15+
import {
16+
getTenantBySubDomain,
17+
getTenantNavigation,
18+
getTenantSiteSettings,
19+
} from "@/lib/data/tenants";
1520
import { getPoliticalEntityBySlug } from "@/lib/data/politicalEntities";
1621
import { getPromiseById } from "@/lib/data/promises";
1722
import { resolveMedia } from "@/lib/data/media";
@@ -53,7 +58,6 @@ export async function generateMetadata({
5358
tenantResolution.context;
5459

5560
const politicalEntity = await getPoliticalEntityBySlug(tenant, entitySlug);
56-
5761
if (!politicalEntity) {
5862
return buildSeoMetadata({
5963
meta: tenantSettings?.meta,
@@ -92,7 +96,7 @@ export async function generateMetadata({
9296
composeTitleSegments(
9397
promise.title?.trim() || tenantTitleBase || tenantSeo.title,
9498
politicalEntity.name,
95-
positionRegion
99+
positionRegion,
96100
) ??
97101
entitySeo.title ??
98102
tenantSeo.title;
@@ -120,7 +124,7 @@ const parseYear = (value?: string | null): number | null => {
120124

121125
const computeTimelineInterval = (
122126
entityPeriod: { from?: string | null; to?: string | null },
123-
statusHistory: { date: string }[]
127+
statusHistory: { date: string }[],
124128
): [number, number] => {
125129
const start = parseYear(entityPeriod.from);
126130
const end = parseYear(entityPeriod.to);
@@ -144,7 +148,7 @@ const computeTimelineInterval = (
144148
};
145149

146150
const buildStatusDocument = (
147-
promise: PromiseDocument
151+
promise: PromiseDocument,
148152
): PromiseStatusDocument | null => {
149153
const relation = promise.status;
150154
if (!relation) {
@@ -215,9 +219,12 @@ export default async function PromiseDetailPage({
215219
]
216220
: [];
217221

222+
const siteSettings = await getTenantSiteSettings(tenant);
223+
224+
const { actNow } = siteSettings || {};
218225
const timelineInterval = computeTimelineInterval(
219226
{ from: entity.periodFrom, to: entity.periodTo },
220-
timelineStatusHistory
227+
timelineStatusHistory,
221228
);
222229

223230
const image = await resolveMedia(promise.image ?? null);
@@ -337,6 +344,7 @@ export default async function PromiseDetailPage({
337344
/>
338345
</Box>
339346
) : null}
347+
<ActNowCard {...(actNow as any)} />
340348
{statusDoc ? (
341349
<Box
342350
sx={{

src/collections/SiteSettings/tabs/EngagementTab.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,55 @@ export const EngagementTab: Tab = {
7474
},
7575
],
7676
},
77+
{
78+
name: "actNow",
79+
type: "group",
80+
label: {
81+
en: "Act Now Section",
82+
fr: "Section Agir maintenant",
83+
},
84+
fields: [
85+
{
86+
name: "title",
87+
type: "text",
88+
required: true,
89+
label: {
90+
en: "Title",
91+
fr: "Titre",
92+
},
93+
},
94+
{
95+
name: "share",
96+
type: "group",
97+
label: {
98+
en: "Share",
99+
fr: "Partager",
100+
},
101+
admin: {
102+
hideGutter: true,
103+
},
104+
fields: [
105+
{
106+
name: "title",
107+
type: "text",
108+
required: true,
109+
label: {
110+
en: "Share Title",
111+
fr: "Partager le titre",
112+
},
113+
},
114+
{
115+
name: "description",
116+
type: "textarea",
117+
required: true,
118+
label: {
119+
en: "Share Description",
120+
fr: "Partager la description",
121+
},
122+
},
123+
],
124+
},
125+
],
126+
},
77127
],
78128
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import CloseIcon from "@mui/icons-material/Close";
2+
import { CardContent, Typography, Grid } from "@mui/material";
3+
import React from "react";
4+
5+
interface Props {
6+
onCloseCard?: () => void;
7+
title?: string;
8+
description?: string;
9+
children?: React.ReactNode;
10+
}
11+
12+
function BaseCard({ onCloseCard, title, description, children }: Props) {
13+
return (
14+
<CardContent
15+
sx={{
16+
padding: 0,
17+
}}
18+
>
19+
{onCloseCard && (
20+
<CloseIcon
21+
onClick={onCloseCard}
22+
sx={{
23+
position: "relative",
24+
top: "30px",
25+
left: "90%",
26+
color: "#c7c7c7",
27+
}}
28+
/>
29+
)}
30+
{title && (
31+
<Grid
32+
sx={{
33+
pt: onCloseCard ? 0 : 4,
34+
}}
35+
>
36+
<Typography align="center" variant="h4">
37+
{title}
38+
</Typography>
39+
</Grid>
40+
)}
41+
{description && (
42+
<Grid>
43+
<Typography
44+
align="center"
45+
variant="body2"
46+
sx={{
47+
fontSize: "15px",
48+
margin: { xs: "10px", sm: 0 },
49+
}}
50+
>
51+
{description}
52+
</Typography>
53+
</Grid>
54+
)}
55+
{children}
56+
</CardContent>
57+
);
58+
}
59+
60+
export default BaseCard;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Grid } from "@mui/material";
2+
import React from "react";
3+
4+
import BaseContent from "./BaseContent";
5+
import CtAButton from "./CtaButton";
6+
export interface ConnectCardProps {
7+
closeCard: () => void;
8+
promiseActNow?: {
9+
connect: {
10+
connectTitle?: string;
11+
connectDescription?: string;
12+
connectButton?: string;
13+
};
14+
};
15+
}
16+
function ConnectCard({
17+
closeCard,
18+
promiseActNow = {
19+
connect: {
20+
connectTitle: "",
21+
connectDescription: "",
22+
connectButton: "",
23+
},
24+
},
25+
}: ConnectCardProps) {
26+
if (!promiseActNow.connect) return null;
27+
const {
28+
connect: { connectTitle, connectDescription, connectButton },
29+
} = promiseActNow;
30+
31+
return (
32+
<BaseContent
33+
onCloseCard={closeCard}
34+
title={connectTitle}
35+
description={connectDescription}
36+
>
37+
<Grid>
38+
<CtAButton color="secondary">{connectButton}</CtAButton>
39+
</Grid>
40+
</BaseContent>
41+
);
42+
}
43+
44+
export default ConnectCard;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Button, Container } from "@mui/material";
2+
import React from "react";
3+
4+
function CtAButton(props: React.ComponentProps<typeof Button>) {
5+
return (
6+
<Container
7+
sx={() => ({
8+
display: "flex",
9+
justifyContent: "center",
10+
margin: { xs: "33px 0", lg: "45px 0" },
11+
})}
12+
>
13+
<Button
14+
variant="contained"
15+
{...props}
16+
sx={({ palette }) => ({
17+
border: `1px solid ${palette.primary.main}`,
18+
minHeight: "48px",
19+
minWidth: { xs: "98px", lg: "158px" },
20+
"&:hover": {
21+
border: `1px solid ${palette.primary.main}`,
22+
background: palette.background.default,
23+
color: palette.text.primary,
24+
},
25+
})}
26+
/>
27+
</Container>
28+
);
29+
}
30+
31+
export default CtAButton;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { TextField, Grid, Box } from "@mui/material";
2+
import React from "react";
3+
4+
import BaseContent from "./BaseContent";
5+
6+
import CtAButton from "./CtaButton";
7+
8+
interface FollowCardProps {
9+
closeCard: () => void;
10+
promiseActNow?: {
11+
follow: {
12+
followTitle?: string;
13+
followDescription?: string;
14+
followButton?: string;
15+
};
16+
};
17+
}
18+
function FollowCard({
19+
closeCard,
20+
promiseActNow = { follow: {} },
21+
}: FollowCardProps) {
22+
if (!promiseActNow.follow) return null;
23+
const {
24+
follow: { followTitle, followDescription, followButton },
25+
} = promiseActNow;
26+
27+
return (
28+
<BaseContent
29+
title={followTitle}
30+
description={followDescription}
31+
onCloseCard={closeCard}
32+
>
33+
<Grid>
34+
<Box
35+
component="form"
36+
sx={{
37+
display: "flex",
38+
justifyContent: "center",
39+
flexDirection: { xs: "column", sm: "row" },
40+
alignItems: "center",
41+
marginTop: { xs: "10px", sm: "30px" },
42+
}}
43+
>
44+
<TextField
45+
id="outlined-basic"
46+
label="example@mail.com"
47+
variant="outlined"
48+
sx={{
49+
margin: { xs: "10px", sm: 0 },
50+
width: { sm: "400px" },
51+
"& .MuiOutlinedInput-root": {
52+
borderRadius: "0px",
53+
border: "none",
54+
},
55+
"& .MuiOutlinedInput-notchedOutline": {
56+
borderColor: "#005DFD",
57+
borderWidth: "2px",
58+
},
59+
"& .MuiInputLabel-outlined": {
60+
color: "#8f8f8f",
61+
top: {
62+
xs: 0,
63+
lg: "-10px",
64+
},
65+
},
66+
}}
67+
/>
68+
<CtAButton
69+
color="primary"
70+
sx={{
71+
margin: "0 0 10px 0",
72+
height: "58px",
73+
width: "auto",
74+
color: "white",
75+
"& :hover": {
76+
backgroundColor: "#015dfd",
77+
},
78+
}}
79+
>
80+
{followButton}
81+
</CtAButton>
82+
</Box>
83+
</Grid>
84+
</BaseContent>
85+
);
86+
}
87+
88+
export default FollowCard;

0 commit comments

Comments
 (0)