Skip to content

Commit

Permalink
Merge pull request #451 from wri/release/magnanimous-mahogany
Browse files Browse the repository at this point in the history
[RELEASE] Magnanimous Mahogany
  • Loading branch information
roguenet committed Aug 22, 2024
2 parents 231a9bf + f81560f commit b6b7c27
Show file tree
Hide file tree
Showing 79 changed files with 2,424 additions and 3,543 deletions.
6 changes: 2 additions & 4 deletions src/admin/apiProvider/dataProviders/userDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ const normalizeUserObject = (item: V2AdminUserRead) => ({
...item,
id: item.uuid as Identifier,
//@ts-ignore
primary_role: item.role,
role: "",
role: item.role,
//@ts-ignore
monitoring_organisations: item?.monitoring_organisations?.map(item => item.uuid)
});
Expand Down Expand Up @@ -124,8 +123,7 @@ export const userDataProvider: UserDataProvider = {
body.organisation = null;
}

delete body.role;
if (!body.primary_role) delete body.primary_role;
if (!body.role) delete body.role;

try {
const resp = await fetchPutV2AdminUsersUUID({ pathParams: { uuid }, body });
Expand Down
38 changes: 17 additions & 21 deletions src/admin/components/Dialogs/FrameworkSelectionDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { CheckCircle, InfoOutlined } from "@mui/icons-material";
import { Button, Dialog, DialogActions, DialogContent, DialogProps, DialogTitle } from "@mui/material";
import { capitalize, split } from "lodash";
import { FC, useCallback, useMemo, useState } from "react";
import { FC, useCallback, useState } from "react";
import { AutocompleteInput, Form } from "react-admin";
import { FieldValues } from "react-hook-form";
import * as yup from "yup";

import { validateForm } from "@/admin/utils/forms";
import { fetchGetV2AdminENTITYExportFRAMEWORK, useGetV2AdminReportingFrameworks } from "@/generated/apiComponents";
import { EntityName, Option } from "@/types/common";
import { useUserFrameworkChoices } from "@/constants/options/userFrameworksChoices";
import { fetchGetV2AdminENTITYExportFRAMEWORK } from "@/generated/apiComponents";
import { EntityName } from "@/types/common";
import { downloadFileBlob } from "@/utils/network";
import { optionToChoices } from "@/utils/options";

interface FrameworkSelectionDialogContentProps {
onCancel: () => void;
Expand All @@ -21,28 +21,18 @@ const validationSchema = yup.object({
});

const FrameworkSelectionDialogContent: FC<FrameworkSelectionDialogContentProps> = ({ onCancel }) => {
const { data: reportingFrameworksData, isLoading: reportingFrameworksLoading } = useGetV2AdminReportingFrameworks({});

const frameworkChoices = useMemo(() => {
const data = reportingFrameworksData?.data?.map(f => ({
title: f.name ?? "",
value: f.access_code ?? ""
})) as Option[];

return optionToChoices(data ?? []);
}, [reportingFrameworksData]);
const frameworkInputChoices = useUserFrameworkChoices();

return (
<>
<DialogTitle>Please select the funding framework you&apos;d like to export.</DialogTitle>
<DialogContent>
<AutocompleteInput
loading={reportingFrameworksLoading}
source="reportingFramework"
label="Reporting Framework"
choices={frameworkChoices}
choices={frameworkInputChoices}
margin="dense"
defaultValue={frameworkChoices[0]?.id}
defaultValue={frameworkInputChoices?.[0]?.id}
disableClearable
fullWidth
/>
Expand All @@ -51,15 +41,15 @@ const FrameworkSelectionDialogContent: FC<FrameworkSelectionDialogContentProps>
<Button onClick={onCancel} startIcon={<InfoOutlined />}>
Cancel
</Button>
<Button type="submit" disabled={reportingFrameworksLoading} startIcon={<CheckCircle />}>
<Button type="submit" disabled={frameworkInputChoices?.length === 0} startIcon={<CheckCircle />}>
Confirm
</Button>
</DialogActions>
</>
);
};

export function useFrameworkExport(entity: EntityName) {
export function useFrameworkExport(entity: EntityName, choices: any[]) {
const [exporting, setExporting] = useState(false);
const [modalOpen, setModalOpen] = useState(false);

Expand All @@ -79,12 +69,18 @@ export function useFrameworkExport(entity: EntityName) {

setModalOpen(false);
},
[entity]
[entity, choices]
);

return {
exporting,
openExportDialog: useCallback(() => setModalOpen(true), []),
onClickExportButton: useCallback(() => {
if (choices?.length > 1) {
setModalOpen(true);
} else {
onExport(choices[0].id);
}
}, [choices]),
frameworkDialogProps: {
open: modalOpen,
onCancel: useCallback(() => setModalOpen(false), []),
Expand Down
11 changes: 9 additions & 2 deletions src/admin/components/ResourceTabs/AuditLogTab/AuditLogTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ const AuditLogTab: FC<IProps> = ({ label, entity, ...rest }) => {
setSelected,
auditLogData,
refetch,
checkPolygonsSite
checkPolygonsSite,
auditData
} = useAuditLogActions({
record,
buttonToggle,
Expand Down Expand Up @@ -94,7 +95,12 @@ const AuditLogTab: FC<IProps> = ({ label, entity, ...rest }) => {
/>
</When>
<When condition={buttonToggle === AuditLogButtonStates.PROJECT && !record?.project}>
<SiteAuditLogProjectStatus record={record} auditLogData={auditLogData} />
<SiteAuditLogProjectStatus
record={record}
auditLogData={auditLogData}
auditData={auditData}
refresh={refetch}
/>
</When>
<When condition={buttonToggle !== AuditLogButtonStates.PROJECT || verifyEntity}>
<SiteAuditLogEntityStatus
Expand All @@ -104,6 +110,7 @@ const AuditLogTab: FC<IProps> = ({ label, entity, ...rest }) => {
refresh={refetch}
buttonToggle={buttonToggle!}
verifyEntity={verifyEntity}
auditData={auditData}
/>
</When>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { FC, Fragment } from "react";
import { useT } from "@transifex/react";
import { useRouter } from "next/router";
import { FC, Fragment, useRef } from "react";
import { When } from "react-if";

import { convertDateFormat } from "@/admin/apiProvider/utils/entryFormat";
import Menu from "@/components/elements/Menu/Menu";
import Text from "@/components/elements/Text/Text";
import Icon, { IconNames } from "@/components/extensive/Icon/Icon";
import { useNotificationContext } from "@/context/notification.provider";
import { useDeleteV2ENTITYUUIDIDDelete } from "@/generated/apiComponents";
import { AuditStatusResponse, V2FileRead } from "@/generated/apiSchemas";

const formattedTextStatus = (text: string) => {
Expand All @@ -22,54 +29,113 @@ const getTextForActionTable = (item: { type: string; status: string; request_rem
}
};

const columnTitles = ["Date", "User", "Action", "Comments", "Attachments"];

const generateUserName = (first_name?: string, last_name?: string): string =>
`${first_name ?? ""} ${last_name ?? ""}`.trim() || "Unknown User";

const AuditLogTable: FC<{ auditLogData: { data: AuditStatusResponse[] } }> = ({ auditLogData }) => (
<>
<div className="grid grid-cols-[14%_20%_15%_30%_21%]">
{columnTitles.map(title => (
<Text key={title} variant="text-12-light" className="border-b border-b-grey-750 text-grey-700">
{title}
</Text>
))}
</div>
<div className="mr-[-7px] grid max-h-[50vh] min-h-[10vh] grid-cols-[14%_20%_15%_30%_21%] overflow-auto pr-[7px]">
{auditLogData?.data?.map((item: AuditStatusResponse, index: number) => (
<Fragment key={index}>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{convertDateFormat(item?.date_created)}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{generateUserName(item.first_name, item.last_name)}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{getTextForActionTable(item as { type: string; status: string; request_removed: boolean })}
const AuditLogTable: FC<{
auditLogData: { data: AuditStatusResponse[] };
auditData?: { entity: string; entity_uuid: string };
refresh?: () => void;
}> = ({ auditLogData, auditData, refresh }) => {
const menuOverflowContainerRef = useRef(null);
const route = useRouter();
const isAdmin = route.asPath.includes("admin");
const columnTitles = isAdmin
? ["Date", "User", "Action", "Comments", "Attachments", ""]
: ["Date", "User", "Action", "Comments", "Attachments"];
const gridColumnSize = isAdmin ? "grid-cols-[14%_20%_15%_27%_19%_5%]" : "grid-cols-[14%_20%_15%_30%_21%]";
const { openNotification } = useNotificationContext();
const t = useT();
const { mutate } = useDeleteV2ENTITYUUIDIDDelete({
onSuccess() {
refresh?.();
openNotification("success", "Success!", t("audit log deleted."));
},
onError: () => {
openNotification("error", "Error!", t("An error occurred while deleting the audit log."));
}
});
const deleteAuditStatus = async (id: string) => {
await mutate({
pathParams: {
entity: auditData?.entity as string,
uuid: auditData?.entity_uuid as string,
id
}
});
};
return (
<>
<div className={`grid ${gridColumnSize}`}>
{columnTitles.map(title => (
<Text key={title} variant="text-12-light" className="border-b border-b-grey-750 text-grey-700">
{title}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2">
{item.comment ?? "-"}
</Text>
<div className="grid max-w-full gap-2 gap-y-1 border-b border-b-grey-750 py-2">
{item?.attachments?.map((attachmentItem: V2FileRead) => (
<Text
key={attachmentItem.uuid}
variant="text-12-light"
className="h-min w-fit max-w-full cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap rounded-xl bg-neutral-40 px-2 py-0.5"
as={"span"}
onClick={() => {
attachmentItem.url && window.open(attachmentItem.url, "_blank");
}}
>
{attachmentItem.file_name}
</Text>
))}
</div>
</Fragment>
))}
</div>
</>
);
))}
</div>
<div
className={`mr-[-7px] grid max-h-[50vh] min-h-[10vh] overflow-auto pr-[7px] ${gridColumnSize}`}
ref={menuOverflowContainerRef}
>
{auditLogData?.data?.map((item: AuditStatusResponse, index: number) => (
<Fragment key={index}>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{convertDateFormat(item?.date_created)}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{generateUserName(item.first_name, item.last_name)}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2 pr-2">
{getTextForActionTable(item as { type: string; status: string; request_removed: boolean })}
</Text>
<Text variant="text-12" className="border-b border-b-grey-750 py-2">
{item.comment ?? "-"}
</Text>
<div className="grid max-w-full gap-2 gap-y-1 border-b border-b-grey-750 py-2">
{item?.attachments?.map((attachmentItem: V2FileRead) => (
<Text
key={attachmentItem.uuid}
variant="text-12-light"
className="h-min w-fit max-w-full cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap rounded-xl bg-neutral-40 px-2 py-0.5"
as={"span"}
onClick={() => {
attachmentItem.url && window.open(attachmentItem.url, "_blank");
}}
>
{attachmentItem.file_name}
</Text>
))}
</div>
<When condition={isAdmin}>
<div className="justify-cente flex items-center border-b border-b-grey-750 py-2">
<Menu
container={menuOverflowContainerRef.current}
className="h-fit cursor-pointer"
menu={[
{
id: "0",
render: () => (
<Text
variant="text-14-semibold"
className="flex items-center"
onClick={() => deleteAuditStatus((item.uuid ?? item.id) as string)}
>
<Icon name={IconNames.TRASH} className="h-4 w-4 lg:h-5 lg:w-5" />
&nbsp; Delete
</Text>
)
}
]}
>
<Icon name={IconNames.ELIPSES} className="h-5 w-5" />
</Menu>
</div>
</When>
</Fragment>
))}
</div>
</>
);
};

export default AuditLogTable;
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface SiteAuditLogEntityStatusProps {
buttonToggle: number;
verifyEntity?: boolean;
viewPD?: boolean;
auditData?: { entity: string; entity_uuid: string };
}

interface SelectedItem {
Expand All @@ -38,7 +39,8 @@ const SiteAuditLogEntityStatus: FC<SiteAuditLogEntityStatusProps> = ({
refresh,
buttonToggle,
verifyEntity,
viewPD = false
viewPD = false,
auditData
}) => {
const isSite = buttonToggle === AuditLogButtonStates.SITE;
const basename = useBasename();
Expand Down Expand Up @@ -79,7 +81,7 @@ const SiteAuditLogEntityStatus: FC<SiteAuditLogEntityStatusProps> = ({
)}
</div>
<When condition={!!auditLogData}>
<AuditLogTable auditLogData={auditLogData!} />
<AuditLogTable auditLogData={auditLogData!} auditData={auditData} refresh={refresh} />
</When>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ import AuditLogTable from "./AuditLogTable";
export interface SiteAuditLogProjectStatusProps {
record?: ProjectLiteRead | null;
auditLogData?: { data: AuditStatusResponse[] };
auditData?: { entity: string; entity_uuid: string };
refresh?: () => void;
}

const SiteAuditLogProjectStatus: FC<SiteAuditLogProjectStatusProps> = ({ record, auditLogData }) => (
const SiteAuditLogProjectStatus: FC<SiteAuditLogProjectStatusProps> = ({
record,
auditLogData,
auditData,
refresh
}) => (
<div className="flex flex-col gap-6">
<div>
<Text variant="text-24-bold" className="mb-1">
Expand All @@ -21,7 +28,7 @@ const SiteAuditLogProjectStatus: FC<SiteAuditLogProjectStatusProps> = ({ record,
</Text>
</div>
<Text variant="text-16-bold">History and Discussion for {record && record?.name}</Text>
{auditLogData && <AuditLogTable auditLogData={auditLogData} />}
{auditLogData && <AuditLogTable auditLogData={auditLogData} auditData={auditData} refresh={refresh} />}
</div>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const ChangeRequestRequestMoreInfoModal = ({
<DialogTitle>{statusTitles[status as IStatus]}</DialogTitle>
</If>
<DialogContent>
<TextInput source="feedback" label="Feedback" fullWidth multiline margin="dense" helperText={false} />
<TextInput source="_feedback" label="Feedback" fullWidth multiline margin="dense" helperText={false} />
<If condition={status === "moreinfo" && feebdackFields.length > 0}>
<AutocompleteArrayInput
source="feedback_fields"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const HighLevelMetics: FC = () => {
<Labeled label="Workdays Created (New Calculation)" sx={inlineLabelSx}>
<NumberField source="workday_count" emptyText="0" />
</Labeled>
<Labeled label="Workdays Created (Combined - PD View)" sx={inlineLabelSx}>
<NumberField source="combined_workday_count" emptyText="0" />
</Labeled>
</ContextCondition>
<Labeled label="Trees Planted" sx={inlineLabelSx}>
<NumberField source="trees_planted_count" emptyText="0" />
Expand Down
Loading

0 comments on commit b6b7c27

Please sign in to comment.