Skip to content

Commit

Permalink
wip: style convention summary page
Browse files Browse the repository at this point in the history
  • Loading branch information
celineung committed Nov 13, 2024
1 parent d33c03e commit c6fb16e
Show file tree
Hide file tree
Showing 12 changed files with 848 additions and 742 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import { fr } from "@codegouvfr/react-dsfr";
import React, { ReactNode, useState } from "react";
import { Badge } from "@codegouvfr/react-dsfr/Badge";
import React, { ReactNode } from "react";
import { ConventionRenewedInformations } from "react-design-system";
import { path, ConventionReadDto, isConventionRenewed } from "shared";
import { sections } from "src/app/contents/admin/conventionValidation";
import {
ColField,
FieldsAndTitle,
RowFields,
} from "src/app/contents/admin/types";
import { Field, Section, SubSection } from "src/app/contents/admin/types";
import { useCopyButton } from "src/app/hooks/useCopyButton";
import { useStyles } from "tss-react/dsfr";
import type { ConventionValidationProps } from "./ConventionValidation";

const cellStyles = {
overflow: "hidden",
whitespace: "nowrap",
};

export const ConventionValidationDetails = ({
convention,
}: ConventionValidationProps) => {
Expand Down Expand Up @@ -55,140 +46,210 @@ export const ConventionValidationDetails = ({
key={index}
convention={convention}
list={list}
index={index}
/>
))}
</>
);
};

const displaySubSectionContent = (
subSection: SubSection,
convention: ConventionReadDto,
) => {
const buildContent = (field: Field): ReactNode => {
let value: React.ReactNode;

if (!field?.key) return;

value = path(field.key, convention) as string;

if (field.getValue) {
value = (
<>
{field.getValue?.(convention)}
{field.copyButton?.(convention)}
</>
);
}

return (
<>
<div className={fr.cx("fr-text--xs", "fr-m-0")}>{field.label}</div>
<div
style={{
fontWeight: "700",
}}
>
{value}
</div>
</>
);
};

if (subSection.isSchedule) {
return subSection.fields
.filter((field) => !!field)
.map((field) => {
return <>{field.getValue?.(convention)}</>;
});
}

return (
<div className={fr.cx("fr-grid-row")}>
{subSection.fields
.filter((field) => !!field)
.filter(
(field) =>
field.getBadgeSeverity ||
!!field.getValue?.(convention) ||
!!path(field.key, convention),
)
.map((field) => {
const badgeSeverity = field.getBadgeSeverity?.(convention);
return (
<>
{convention.status !== "DRAFT" &&
badgeSeverity &&
field.getValue && (
<div
key={field.key}
className={fr.cx("fr-col-12", "fr-mb-2w")}
>
<Badge severity={badgeSeverity}>
{badgeSeverity === "success"
? `Signée - Le ${field.getValue(convention)}`
: "Signature en attente"}
</Badge>
</div>
)}
{!field.getBadgeSeverity && (
<section
className={fr.cx("fr-col-12", "fr-col-sm-6", "fr-mb-2w")}
key={field.key}
>
{buildContent(field)}
</section>
)}
</>
);
})}
</div>
);
};

export const ConventionValidationSection = ({
convention,
list,
index,
}: {
convention: ConventionReadDto;
list: FieldsAndTitle;
index: number;
list: Section;
}) => {
const { cx } = useStyles();
const [markedAsRead, setMarkedAsRead] = useState<boolean>(false);
const relevantSection = list.rowFields.filter(
(row) =>
row.fields.filter((field) => {
if (!field) return false;

const buildContent = (field: ColField): ReactNode => {
let value: React.ReactNode;
if (field?.key) {
value = path(field.key, convention) as string;
if (field.getValue) {
value = (
<>
{field.getValue?.(convention)}
{field.copyButton?.(convention)}
</>
);
}
}
const pathValue = path(field.key, convention);
const fieldValue = field?.getValue?.(convention);

if (pathValue === undefined) return false;
return fieldValue ?? pathValue;
}).length,
);

return value;
const shouldDisplayHorizontalSeparator = (
index: number,
hasBackgroundColor: boolean,
isFullWidthDisplay: boolean,
) => {
if (hasBackgroundColor) return false;
if (isFullWidthDisplay) return true;
return index % 2 === 0;
};

const renderRows = (rowFields: RowFields[]) => {
const relevantRows = rowFields.filter(
(row) =>
row.fields.filter((field) => {
if (!field) return false;
const shouldDisplayVerticalSeparator = (
index: number,
isFullWidthDisplay: boolean,
) => {
if (isFullWidthDisplay) return false;
return index % 2 === 0;
};

const pathValue = path(field.key, convention);
const fieldValue = field?.getValue?.(convention);
const computeAdditionalStyles = (
index: number,
subSection: SubSection,
): React.CSSProperties => {
const backgroundColorStyles: React.CSSProperties =
subSection.hasBackgroundColor
? {
backgroundColor: "var(--background-alt-blue-france)",
}
: {};

if (pathValue === undefined) return false;
return fieldValue ?? pathValue;
}).length,
);
const borderStyles: React.CSSProperties = shouldDisplayVerticalSeparator(
index,
!!subSection.isFullWidthDisplay,
)
? {
borderRight: "1px solid var(--grey-900-175)",
}
: {};

return relevantRows.map(
(row, index) =>
row.fields.length > 0 && (
<tr key={row.title ?? index}>
{row.title && (
<td style={cellStyles}>
<strong>{row.title}</strong>
</td>
)}

{row.fields.map((field, index) =>
field ? (
<td key={field.key} style={cellStyles}>
{buildContent(field)}
</td>
) : (
// biome-ignore lint/suspicious/noArrayIndexKey: Index is ok here
<td key={index} />
),
)}
</tr>
),
);
return {
...borderStyles,
...backgroundColorStyles,
};
};

return (
<div
className={cx(
fr.cx("fr-table", "fr-table--bordered"),
list.additionalClasses,
)}
key={list.listTitle}
<section
className={fr.cx("fr-mb-3w", "fr-p-2w")}
style={{
border: "1px solid var(--grey-900-175)",
borderRadius: "8px",
}}
>
<table>
<caption
style={{
display: "flex",
justifyContent: "space-between",
width: "100%",
}}
>
{list.listTitle}
<div className={fr.cx("fr-toggle")}>
<input
type="checkbox"
onChange={() => setMarkedAsRead((read) => !read)}
className={fr.cx("fr-toggle__input")}
id={`fr-toggle__input-${index}`}
checked={markedAsRead}
/>
<label
className={fr.cx("fr-toggle__label")}
htmlFor={`fr-toggle__input-${index}`}
>
{markedAsRead ? "Vérifier à nouveau" : "Marquer comme vu"}
</label>
</div>
</caption>

{!markedAsRead && (
<>
<thead>
<tr>
{list.cols?.map((col, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: Index is ok here
<th key={index} scope="col">
{col}
</th>
))}
{!list.cols &&
list.rowFields[0] &&
list.rowFields[0].fields.map((field) =>
field ? (
<th key={field.key} scope="col">
{field.colLabel}
</th>
) : null,
)}
</tr>
</thead>
<tbody>{renderRows(list.rowFields)}</tbody>
</>
)}
</table>
</div>
<h2 className={fr.cx("fr-mt-3v", "fr-mb-0")}>{list.title}</h2>
<div className={fr.cx("fr-grid-row")}>
{relevantSection.map((subSection, index) => {
return (
<>
{shouldDisplayHorizontalSeparator(
index,
!!subSection.hasBackgroundColor,
!!subSection.isFullWidthDisplay,
) && (
<div
className={fr.cx("fr-my-2w")}
style={{
width: "100%",
height: "1px",
backgroundColor: "var(--grey-900-175)",
}}
/>
)}
<section
className={fr.cx(
"fr-col-12",
!subSection.isFullWidthDisplay && "fr-col-md-6",
subSection.hasBackgroundColor && "fr-p-2w",
subSection.hasBackgroundColor && "fr-mt-2w",
)}
key={subSection.title}
style={computeAdditionalStyles(index, subSection)}
>
{subSection.getTitle && (
<h3>{subSection.getTitle(convention)}</h3>
)}
{!subSection.getTitle && subSection.title && (
<h3>{subSection.title}</h3>
)}
{displaySubSectionContent(subSection, convention)}
</section>
</>
);
})}
</div>
</section>
);
};
Loading

0 comments on commit c6fb16e

Please sign in to comment.