From bd6f5c6365df37c1f7e5efa68c57ae3a32eef806 Mon Sep 17 00:00:00 2001 From: roshni73 Date: Thu, 5 Jun 2025 12:20:00 +0545 Subject: [PATCH 01/13] Add Report table and report modal to add and edit --- src/App/routes/index.tsx | 2 +- src/index.css | 3 + src/views/Reports/ReportActions/index.tsx | 156 ++++++++ .../Reports/ReportActions/styles.module.css | 6 + src/views/Reports/ReportsModal/index.tsx | 348 ++++++++++++++++++ .../Reports/ReportsModal/styles.module.css | 7 + src/views/Reports/index.tsx | 269 ++++++++++++++ src/views/Reports/styles.module.css | 27 ++ 8 files changed, 817 insertions(+), 1 deletion(-) create mode 100644 src/views/Reports/ReportActions/index.tsx create mode 100644 src/views/Reports/ReportActions/styles.module.css create mode 100644 src/views/Reports/ReportsModal/index.tsx create mode 100644 src/views/Reports/ReportsModal/styles.module.css diff --git a/src/App/routes/index.tsx b/src/App/routes/index.tsx index 0f7d742..54b897a 100644 --- a/src/App/routes/index.tsx +++ b/src/App/routes/index.tsx @@ -162,7 +162,6 @@ const events = customWrapRoute({ visibility: 'is-authenticated', }, }); - /* const userManagement = customWrapRoute({ parent: homeLayout, @@ -301,6 +300,7 @@ const wrappedRoutes = { reports, podcast, youtubeVideos, + // userActivation, // editProfile, // forgotPassword, diff --git a/src/index.css b/src/index.css index 025a6eb..f038fde 100644 --- a/src/index.css +++ b/src/index.css @@ -59,6 +59,9 @@ --cms-ui-border-radius-3xl: 1.5rem; --cms-ui-border-radius-full: 9999px; + + --cms-color-background-input-section-light: rgba(0, 29, 71, .1); + --cms-ui-color-blue-20: #F5FAFF; --cms-ui-color-blue-50: #EDF6FF; --cms-ui-color-blue-100: #CFE5FB; diff --git a/src/views/Reports/ReportActions/index.tsx b/src/views/Reports/ReportActions/index.tsx new file mode 100644 index 0000000..b4a3875 --- /dev/null +++ b/src/views/Reports/ReportActions/index.tsx @@ -0,0 +1,156 @@ +import { useCallback } from 'react'; +import { + IoPencil, + IoTrash, +} from 'react-icons/io5'; +import { + gql, + useMutation, +} from '@apollo/client'; +import { Button } from '@togglecorp/toggle-ui'; + +import { + ArchiveReportMutation, + ArchiveReportMutationVariables, + ReportTypeMutationResponseType, +} from '#generated/types/graphql'; +import useAlert from '#hooks/useAlert'; +import useBooleanState from '#hooks/useBooleanState'; + +import ReportModal from '../ReportsModal'; + +import styles from './styles.module.css'; + +interface Props { + id: string; + onDelete: (id: string) => void; +} + +const ARCHIVE_REPORT = gql` + mutation ArchiveReport($pk: ID!) { + archiveReport(pk: $pk) { + ... on ReportTypeMutationResponseType { + errors + ok + result { + description + id + isDeleted + publishedDate + reportFile { + url + } + status + title + } + } + ... on OperationInfo { + __typename + messages { + message + } + } + } + } +`; +function ReportActions(props: Props) { + const { + id, + onDelete, + } = props; + const alert = useAlert(); + + const [ + showEditReportModal, { + setTrue: setShowEditReportModalTrue, + setFalse: setShowEditReportModalFalse, + }] = useBooleanState(false); + + const [ + triggerArchiveReport, + { loading: archiveLoading }, + ] = useMutation( + ARCHIVE_REPORT, + { + onCompleted: (response) => { + const archiveEvent = response.archiveReport as ReportTypeMutationResponseType; + const { ok, errors } = archiveEvent; + if (errors) { + const errorMessages = errors + ?.map((message: { messages: string; }) => message.messages) + .filter((msg: string) => msg) + .join(', '); + alert.show(errorMessages); + } else if (ok) { + alert.show( + 'Successfully Archived the Report', + { variant: 'success' }, + ); + onDelete(id); + } + }, + onError: () => { + alert.show( + 'Failed to Archive the Report', + { variant: 'danger' }, + ); + }, + }, + ); + + const handleDelete = useCallback(() => { + if (!id) { + alert.show('Invalid report ID'); + return; + } + triggerArchiveReport({ variables: { pk: id } }); + }, [triggerArchiveReport, id, alert]); + + const handleEdit = useCallback(() => { + if (!id) { + alert.show('Invalid report ID'); + return; + } + setShowEditReportModalTrue(); + }, [id, alert, setShowEditReportModalTrue]); + + const initialValues = { + id, + title: '', + description: '', + publishedDate: '', + status: '', + reportFile: undefined, + }; + + return ( +
+ + + {showEditReportModal && ( + + )} +
+ ); +} + +export default ReportActions; diff --git a/src/views/Reports/ReportActions/styles.module.css b/src/views/Reports/ReportActions/styles.module.css new file mode 100644 index 0000000..4542413 --- /dev/null +++ b/src/views/Reports/ReportActions/styles.module.css @@ -0,0 +1,6 @@ +.report-actions{ + display :flex; + flex-direction:row; + gap: var(--cms-ui-spacing-sm); + padding: var(--cms-ui-spacing-xs) var(--cms-ui-spacing-xs) var(--cms-ui-spacing-xs) var(--cms-ui-spacing-md); +} \ No newline at end of file diff --git a/src/views/Reports/ReportsModal/index.tsx b/src/views/Reports/ReportsModal/index.tsx new file mode 100644 index 0000000..8508de3 --- /dev/null +++ b/src/views/Reports/ReportsModal/index.tsx @@ -0,0 +1,348 @@ +import { + useCallback, + useState, +} from 'react'; +import { + gql, + useMutation, +} from '@apollo/client'; +import { + createSubmitHandler, + getErrorObject, + ObjectSchema, + requiredStringCondition, + useForm, +} from '@togglecorp/toggle-form'; +import { + Button, + DateInput, + Modal, + SelectInput, + TextArea, + TextInput, +} from '@togglecorp/toggle-ui'; + +import { + CreateReportInput, + CreateReportMutation, + CreateReportMutationVariables, + ReportTypeMutationResponseType, + UpdateReportInput, + UpdateReportMutation, + UpdateReportMutationVariables, +} from '#generated/types/graphql'; +import useAlert from '#hooks/useAlert'; + +import styles from './styles.module.css'; + +const CREATE_REPORT = gql` + mutation CreateReport($data: CreateReportInput!) { + createReport(data: $data) { + ... on ReportTypeMutationResponseType { + errors + ok + result { + description + id + publishedDate + isDeleted + reportFile { + url + } + status + title + } + } + ... on OperationInfo { + __typename + messages { + message + } + } + } + } +`; + +const UPDATE_REPORT = gql` + mutation UpdateReport($pk: ID!, $data: UpdateReportInput!) { + updateReport(pk: $pk, data: $data) { + ... on ReportTypeMutationResponseType { + errors + ok + result { + description + id + publishedDate + isDeleted + reportFile { + url + } + status + title + } + } + ... on OperationInfo { + __typename + messages { + message + } + } + } + } +`; + +interface Props { + onClose: () => void; + title: string; + initialValues?: Partial; +} + +const statusOptions = [ + { key: 'DRAFT', label: 'Draft' }, + { key: 'PUBLISHED', label: 'Published' }, +]; + +type PartialFormType = Partial; +type FormSchema = ObjectSchema; +type FormSchemaFields = ReturnType; + +const keySelector = (option: { key: string }) => option.key; +const labelSelector = (option: { label: string }) => option.label; + +const formSchema: FormSchema = { + fields: (): FormSchemaFields => ({ + title: { + required: true, + requiredValidation: requiredStringCondition, + }, + description: { + required: true, + requiredValidation: requiredStringCondition, + }, + publishedDate: { + required: true, + requiredValidation: requiredStringCondition, + }, + status: { + required: true, + requiredValidation: requiredStringCondition, + }, + reportFile: {}, + }), +}; + +function ReportModal(props: Props) { + const { + onClose, + title, + initialValues, + } = props; + const alert = useAlert(); + + const defaultFormValues: PartialFormType = initialValues || { + title: '', + description: '', + publishedDate: '', + status: 'DRAFT', + reportFile: undefined, + }; + + const [filePreview, setFilePreview] = useState( + initialValues?.reportFile ? initialValues.reportFile : undefined, + ); + + const { + value, + error: formError, + pristine, + setFieldValue, + setError, + validate, + } = useForm(formSchema, { value: defaultFormValues }); + + const [ + createReportTrigger, + { loading: createReportLoading }, + ] = useMutation( + CREATE_REPORT, + { + onCompleted: (response) => { + const createReport = response.createReport as ReportTypeMutationResponseType; + const { ok, errors } = createReport; + if (errors) { + const errorMessages = errors + ?.map((message: { messages: string; }) => message.messages) + .filter((msg: string) => msg) + .join(', '); + alert.show(errorMessages); + } else if (ok) { + alert.show( + 'Report Successfully created', + { variant: 'success' }, + ); + onClose(); + } + }, + onError: () => { + alert.show( + 'Failed to Create Report', + { variant: 'danger' }, + ); + }, + }, + ); + + const [ + updateReportTrigger, + { loading: updateReportLoading }, + ] = useMutation( + UPDATE_REPORT, + { + onCompleted: (response) => { + const updateReport = response.updateReport as ReportTypeMutationResponseType; + const { ok, errors } = updateReport; + if (errors) { + const errorMessages = errors + ?.map((message: { messages: string; }) => message.messages) + .filter((msg: string) => msg) + .join(', '); + alert.show(errorMessages); + } else if (ok) { + alert.show( + 'Report Successfully updated', + { variant: 'success' }, + ); + onClose(); + } + }, + onError: () => { + alert.show( + 'Failed to Update Report', + { variant: 'danger' }, + ); + }, + }, + ); + + const handleFileChange = useCallback( + (e: React.ChangeEvent) => { + const uploadedFile = e.target.files?.[0] ?? null; + setFieldValue(uploadedFile, 'reportFile'); + if (uploadedFile) { + setFilePreview(uploadedFile.name); + } else { + setFilePreview(undefined); + } + }, + [setFieldValue], + ); + + const handleSubmit = useCallback(() => { + createSubmitHandler(validate, setError, (finalValue: PartialFormType) => { + if (initialValues?.id) { + updateReportTrigger({ + variables: { + pk: initialValues.id, + data: finalValue as UpdateReportInput, + }, + context: { + hasUpload: true, + }, + }); + } else { + createReportTrigger({ + variables: { + data: finalValue as CreateReportInput, + }, + context: { + hasUpload: true, + }, + }); + } + })(); + }, [validate, setError, createReportTrigger, updateReportTrigger, initialValues]); + + const error = getErrorObject(formError); + + return ( + + + + + )} + freeHeight + > + +