Skip to content

Commit 03dbd0e

Browse files
authored
Merge pull request #2667 from tekdi/release-1.15.0
Release 1.15.0 to admin dev
2 parents c7155c6 + f4af2df commit 03dbd0e

File tree

15 files changed

+947
-59
lines changed

15 files changed

+947
-59
lines changed

apps/admin-app-repo/src/pages/centers.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
} from '@/services/CohortService/cohortService';
3333
import ConfirmationPopup from '@/components/ConfirmationPopup';
3434
import { updateCohort } from '@/services/MasterDataService';
35-
import { transformLabel } from '@/utils/Helper';
35+
import { transformLabel, transformLabelWithoutSpaces } from '@/utils/Helper';
3636
import { useTheme } from '@mui/material/styles';
3737
import AddIcon from '@mui/icons-material/Add';
3838
import CenteredLoader from '@/components/CenteredLoader/CenteredLoader';
@@ -279,7 +279,7 @@ const Centers = () => {
279279
{
280280
key: 'name',
281281
label: 'Center Name',
282-
render: (row: any) => transformLabel(row?.name),
282+
render: (row: any) => transformLabelWithoutSpaces(row?.name),
283283
},
284284
{
285285
key: 'address',

apps/admin-app-repo/src/utils/Helper.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ export const transformLabel = (label: string): string => {
139139
.replace(/\b\w/g, (char) => char.toUpperCase()); // Capitalize the first letter of each word
140140
};
141141

142+
export const transformLabelWithoutSpaces = (label: string): string => {
143+
if (typeof label !== 'string') {
144+
return label as any;
145+
}
146+
147+
return label
148+
.toLowerCase()
149+
.replace(/\b\w/g, (char) => char.toUpperCase());
150+
};
151+
152+
142153
export const transformArray = (arr: State[]): State[] => {
143154
if (!arr || !Array.isArray(arr)) {
144155
return arr;

apps/learner-web-app/src/app/landing/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export default function LandingPage() {
134134
};
135135

136136
const aboutPrathamText = t('LANDING.ABOUT_PRATHAM_TEXT');
137+
const aboutPrathamLearningPlatformText = t('LANDING.ABOUT_PRATHAM_LEARNING_PLATFORM_TEXT');
137138

138139
return (
139140
<>
@@ -395,7 +396,7 @@ export default function LandingPage() {
395396
whiteSpace: 'pre-line',
396397
}}
397398
>
398-
{aboutPrathamText}
399+
{aboutPrathamLearningPlatformText}
399400
</Typography>
400401
</Grid>
401402
</Grid>

apps/learner-web-app/src/components/EditProfile/EditProfile.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ const EditProfile = ({ completeProfile, enrolledProgram, uponEnrollCompletion }:
144144
delete responseFormForEnroll?.schema?.properties?.district;
145145
delete responseFormForEnroll?.schema?.properties?.block;
146146
delete responseFormForEnroll?.schema?.properties?.village;
147+
delete responseFormForEnroll?.schema?.properties?.consent_file;
147148
responseFormForEnroll?.schema?.required?.pop('batch');
148149

149150
const responseFormCopy = JSON.parse(JSON.stringify(responseForm));

apps/learner-web-app/src/components/UserProfileCard/UserProfileCard.tsx

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,24 @@ import {
99
Menu,
1010
MenuItem,
1111
IconButton,
12+
Dialog,
13+
DialogTitle,
14+
DialogContent,
15+
DialogActions,
16+
Button,
1217
} from '@mui/material';
1318
import { useEffect, useState } from 'react';
1419
import settingImage from '../../../public/images/settings.png';
1520
import Image from 'next/image';
1621
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
22+
import VisibilityIcon from '@mui/icons-material/Visibility';
1723
import { useRouter } from 'next/navigation';
1824
import { getUserDetails } from '@learner/utils/API/userService';
1925
import { Loader, useTranslation } from '@shared-lib';
2026
import { isUnderEighteen, toPascalCase } from '@learner/utils/helper';
2127
import { fetchForm } from '@shared-lib-v2/DynamicForm/components/DynamicFormCallback';
2228
import { FormContext } from '@shared-lib-v2/DynamicForm/components/DynamicFormConstant';
29+
import DocumentViewer from '@shared-lib-v2/DynamicForm/components/DocumentViewer/DocumentViewer';
2330

2431
// Helper function to get field value from userData based on schema
2532
const getFieldValue = (fieldName: string, fieldSchema: Record<string, unknown>, userData: Record<string, unknown>, customFields: Array<Record<string, unknown>> = []) => {
@@ -104,6 +111,9 @@ const UserProfileCard = ({ maxWidth = '600px' }) => {
104111
const [formSchema, setFormSchema] = useState<Record<string, unknown> | null>(null); // Form schema state
105112
const { t } = useTranslation();
106113
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
114+
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
115+
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
116+
const [previewTitle, setPreviewTitle] = useState<string>('');
107117

108118
const storedConfig =
109119
typeof window !== 'undefined'
@@ -184,6 +194,39 @@ const UserProfileCard = ({ maxWidth = '600px' }) => {
184194
setAnchorEl(null);
185195
};
186196

197+
const handlePreview = (url: string, title: string) => {
198+
setPreviewUrl(url);
199+
setPreviewTitle(title);
200+
setIsPreviewOpen(true);
201+
};
202+
203+
const handleClosePreview = () => {
204+
setIsPreviewOpen(false);
205+
setPreviewUrl(null);
206+
setPreviewTitle('');
207+
};
208+
209+
// Helper function to check if a field is a file field
210+
const isFileField = (fieldSchema: Record<string, unknown>): boolean => {
211+
return (
212+
fieldSchema?.field_type === 'file_upload' ||
213+
fieldSchema?.field_type === 'file' ||
214+
fieldSchema?.type === 'array' && (fieldSchema?.items as Record<string, unknown>)?.type === 'string' &&
215+
((fieldSchema?.items as Record<string, unknown>)?.format === 'data-url' || (fieldSchema?.items as Record<string, unknown>)?.format === 'uri')
216+
);
217+
};
218+
219+
// Helper function to check if a value is a valid URL
220+
const isValidUrl = (value: unknown): boolean => {
221+
if (typeof value !== 'string') return false;
222+
try {
223+
const url = new URL(value);
224+
return url.protocol === 'http:' || url.protocol === 'https:';
225+
} catch {
226+
return false;
227+
}
228+
};
229+
187230
const handleOpen = (option: string) => {
188231
console.log(option);
189232
if (option === t('LEARNER_APP.USER_PROFILE_CARD.EDIT_PROFILE')) {
@@ -586,13 +629,46 @@ const UserProfileCard = ({ maxWidth = '600px' }) => {
586629
{otherSectionFields.map((field) => {
587630
const fieldTitle = (field.schema.title as string) || field.name;
588631
const labelKey = `FORM.${fieldTitle}`;
632+
const isFile = isFileField(field.schema);
633+
const hasValidUrl = isValidUrl(field.rawValue);
589634

590635
return (
591636
<Grid item xs={6} key={field.name}>
592637
<Typography sx={labelStyle}>
593638
{t(labelKey, { defaultValue: toPascalCase(String(fieldTitle).replace(/_/g, ' ')) })}
594639
</Typography>
595-
<Typography sx={valueStyle}>{t(`FORM.${String(field.value).toUpperCase()}`, { defaultValue: toPascalCase(String(field.value)) })}</Typography>
640+
{isFile && hasValidUrl ? (
641+
<Box
642+
sx={{
643+
display: 'flex',
644+
alignItems: 'center',
645+
gap: 0.5,
646+
cursor: 'pointer',
647+
'&:hover': {
648+
'& .view-text': {
649+
textDecoration: 'underline',
650+
}
651+
}
652+
}}
653+
onClick={() => handlePreview(String(field.rawValue), fieldTitle)}
654+
>
655+
<VisibilityIcon sx={{ fontSize: '1rem', color: 'primary.main' }} />
656+
<Typography
657+
className="view-text"
658+
sx={{
659+
...valueStyle,
660+
color: 'primary.main',
661+
fontWeight: 500
662+
}}
663+
>
664+
{t('VIEW_FILE', { defaultValue: 'View File' })}
665+
</Typography>
666+
</Box>
667+
) : (
668+
<Typography sx={valueStyle}>
669+
{t(`FORM.${String(field.value).toUpperCase()}`, { defaultValue: toPascalCase(String(field.value)) })}
670+
</Typography>
671+
)}
596672
</Grid>
597673
);
598674
})}
@@ -637,6 +713,56 @@ const UserProfileCard = ({ maxWidth = '600px' }) => {
637713
>
638714
Open PDF
639715
</a> */}
716+
717+
{/* File Preview Dialog */}
718+
<Dialog
719+
open={isPreviewOpen}
720+
onClose={handleClosePreview}
721+
maxWidth="lg"
722+
fullWidth
723+
PaperProps={{
724+
sx: {
725+
height: '90vh',
726+
maxHeight: '90vh',
727+
},
728+
}}
729+
>
730+
<DialogTitle>
731+
{t('FILE_PREVIEW', { defaultValue: 'File Preview' })} - {t(`FORM.${previewTitle}`, { defaultValue: toPascalCase(String(previewTitle).replace(/_/g, ' ')) })}
732+
</DialogTitle>
733+
<DialogContent
734+
sx={{
735+
p: 0,
736+
display: 'flex',
737+
flexDirection: 'column',
738+
overflow: 'hidden',
739+
}}
740+
>
741+
{previewUrl && (
742+
<DocumentViewer
743+
url={previewUrl}
744+
width="100%"
745+
height="100%"
746+
showError={true}
747+
showDownloadButton={false}
748+
/>
749+
)}
750+
</DialogContent>
751+
<DialogActions>
752+
<Button onClick={handleClosePreview}>{t('COMMON.CLOSE', { defaultValue: 'Close' })}</Button>
753+
{/* {previewUrl && (
754+
<Button
755+
variant="contained"
756+
href={previewUrl}
757+
download
758+
target="_blank"
759+
rel="noopener noreferrer"
760+
>
761+
{t('COMMON.DOWNLOAD', { defaultValue: 'Download' })}
762+
</Button> */}
763+
764+
</DialogActions>
765+
</Dialog>
640766
</Box>
641767
);
642768
};

apps/teachers/public/locales/en/common.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
"CLOSE": "Close",
5353
"CANCEL": "Cancel",
5454
"OK": "OK",
55+
"VIEW_FILE": "View File",
56+
"FILE_PREVIEW": "File Preview",
57+
"DOWNLOAD": "Download",
5558
"FROM_TO_DATE": "From - To dates",
5659
"REMOVE": "Remove",
5760
"SEND_REQUEST": "Send Request",

libs/shared-lib-v2/src/DynamicForm/components/DocumentViewer/DocumentViewer.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ interface DocumentViewerProps {
3131
width?: string | number;
3232
height?: string | number;
3333
showError?: boolean;
34+
showDownloadButton?: boolean;
3435
}
3536

3637
const DocumentViewer: React.FC<DocumentViewerProps> = ({
3738
url,
3839
width = '100%',
3940
height = '600px',
4041
showError = true,
42+
showDownloadButton = true,
4143
}) => {
4244
const [loading, setLoading] = useState(true);
4345
const [error, setError] = useState<string | null>(null);
@@ -626,16 +628,18 @@ const DocumentViewer: React.FC<DocumentViewerProps> = ({
626628
</>
627629
)}
628630
</Box>
629-
<IconButton
630-
size="small"
631-
href={url}
632-
download
633-
target="_blank"
634-
rel="noopener noreferrer"
635-
title="Download"
636-
>
637-
<Download />
638-
</IconButton>
631+
{showDownloadButton && (
632+
<IconButton
633+
size="small"
634+
href={url}
635+
download
636+
target="_blank"
637+
rel="noopener noreferrer"
638+
title="Download"
639+
>
640+
<Download />
641+
</IconButton>
642+
)}
639643
</Box>
640644
)}
641645

0 commit comments

Comments
 (0)