Skip to content

Commit

Permalink
Issue #PS-000 feat: Form create using rjsf
Browse files Browse the repository at this point in the history
  • Loading branch information
itsvick committed Jul 16, 2024
1 parent e387f82 commit 7351c67
Show file tree
Hide file tree
Showing 15 changed files with 908 additions and 306 deletions.
10 changes: 10 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,13 @@ export const accessControl: { [key: string]: Role[] } = {
showBlockLevelCenterData: [Role.TEAM_LEADER],
showTeacherLevelCenterData: [Role.TEACHER],
};

export const fullWidthPages = [
'/login',
'/forgot-password',
'/reset-password',
'/404',
'/500',
'/offline',
'/unauthorized',
];
5 changes: 4 additions & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@
"UPCOMING_EXTRA_SESSION": "Upcoming Extra Sessions",
"NO_SESSIONS_SCHEDULED": "No sessions scheduled",
"TO_BE_TAUGHT": "What is going to be taught:",
"SELECT_TOPIC": "Select Topic, Sub-topic"
"SELECT_TOPIC": "Select Topic, Sub-topic",
"PAGE_NOT_FOUND": "पृष्ठ नहीं मिला",
"ACCESS_DENIED": "पहुंच निषेधित",
"YOU_DONT_HAVE_PERMISSION_TO_ACCESS_THIS_PAGE": "आपके पास इस पृष्ठ तक पहुंचने की अनुमति नहीं है"
},
"LOGIN_PAGE": {
"USERNAME": "Username",
Expand Down
5 changes: 4 additions & 1 deletion public/locales/or/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@
"SURE_REMOVE": "ଆପଣ ନିଶ୍ଚିତ ଏହି ଶିକ୍ଷାର୍ଥୀକୁ ହଟାଇବାକୁ ଚାହୁଁଛନ୍ତି କି?",
"LEARNER_MARKED_DROPOUT": "ଶିକ୍ଷାର୍ଥୀକୁ ଡ୍ରପ୍ ଆଉଟ୍ ଭାବେ ଚିହ୍ନିତ କରାଯାଇଛି",
"LEARNER_UNMARKED_DROPOUT": "ଶିକ୍ଷାର୍ଥୀକୁ ଡ୍ରପ୍ ଆଉଟ୍ ଭାବେ ଅଚିହ୍ନିତ କରାଯାଇଛି",
"LEARNER_REMOVED": "ଶିକ୍ଷାର୍ଥୀ କୁ ହଟାଇ ଦିଆଯାଇଛି"
"LEARNER_REMOVED": "ଶିକ୍ଷାର୍ଥୀ କୁ ହଟାଇ ଦିଆଯାଇଛି",
"PAGE_NOT_FOUND": "ପୃଷ୍ଠା ଖୋଜିପାରିବନାହି",
"ACCESS_DENIED": "ପ୍ରବେଶ ନିଷେଧ",
"YOU_DONT_HAVE_PERMISSION_TO_ACCESS_THIS_PAGE": "ଆପଣଙ୍କୁ ଏହି ପୃଷ୍ଠା ପ୍ରବେଶ କରିବା ଅଧିକାର ନାହି"
},
"LOGIN_PAGE": {
"USERNAME": "ଉପଯୋଗକର୍ତା ନାମ",
Expand Down
18 changes: 17 additions & 1 deletion src/components/DynamicForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,35 @@ const DynamicForm: React.FC<DynamicFormProps> = ({
onError(errors);
};

function transformErrors(errors: any) {
return errors.map((error: any) => {
if (error.name === 'pattern') {
error.message = 'Only digits are allowed';
}
return error;
});
}


function handleChange(event: any) {
console.log('Form data changed:', event);
onChange(event);
}

return (
<FormWithMaterialUI
schema={schema}
uiSchema={uiSchema}
formData={formData}
onChange={onChange}
onChange={handleChange}
onSubmit={onSubmit}
validator={validator}
liveValidate
showErrorList={false}
widgets={widgets}
noHtml5Validate
onError={handleError}
transformErrors={transformErrors}
// ErrorList={CustomErrorList}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { UiSchema } from '@rjsf/utils';
import { JSONSchema7 } from 'json-schema';
import { apiResponse } from '@/utils/schema';
import { i18n } from 'next-i18next';
import { getTranslatedText } from '@/utils/Helper';

interface FieldOption {
label: string;
Expand All @@ -24,10 +26,12 @@ interface Field {
minLength?: number | null;
fieldId: string;
dependsOn: boolean;
required?: boolean;
}

const GenerateSchemaAndUiSchema = (apiResponse: any) => {
const schema: JSONSchema7 = {

const schema: JSONSchema7 = { //Form schema
title: 'A registration form',
description: 'A simple form example',
type: 'object',
Expand All @@ -36,9 +40,9 @@ const GenerateSchemaAndUiSchema = (apiResponse: any) => {
dependencies: {},
};

const uiSchema: UiSchema = {};
const uiSchema: UiSchema = {}; //form ui schema

apiResponse.result.forEach((field: Field) => {
apiResponse.fields.forEach((field: Field) => {
const {
label,
name,
Expand All @@ -49,10 +53,12 @@ const GenerateSchemaAndUiSchema = (apiResponse: any) => {
isMultiSelect,
maxSelections,
dependsOn,
pattern,
required
} = field;

const fieldSchema: any = {
title: label,
title: getTranslatedText(label),
};

const fieldUiSchema: any = {};
Expand Down Expand Up @@ -130,6 +136,23 @@ const GenerateSchemaAndUiSchema = (apiResponse: any) => {
fieldUiSchema['ui:widget'] = 'MultiSelectCheckboxes';
}

if (pattern) {
fieldSchema.pattern = pattern;
fieldUiSchema["ui:help"]= "Only alphabetic characters are allowed.";
}

if (required) {
schema.required?.push(name);
}

if (field?.minLength) {
fieldSchema.minLength = Number(field.minLength);
}

if (field?.maxLength) {
fieldSchema.maxLength = Number(field.maxLength);
}

if (schema !== undefined && schema.properties) {
schema.properties[name] = fieldSchema;
uiSchema[name] = fieldUiSchema;
Expand All @@ -140,5 +163,6 @@ const GenerateSchemaAndUiSchema = (apiResponse: any) => {
};

const { schema, uiSchema } = GenerateSchemaAndUiSchema(apiResponse);
console.log(schema, uiSchema);

export { schema, uiSchema };
15 changes: 14 additions & 1 deletion src/pages/404.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import ErrorIcon from '../../public/images/404.png'; // Make sure to replace this with the actual path to your image
import Image from 'next/image';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';

const PageNotFound = () => {
const { t } = useTranslation();

return (
<Box
py={4}
Expand All @@ -23,10 +27,19 @@ const PageNotFound = () => {
fontWeight="600"
color="black"
>
Page not Found
{t('COMMON.PAGE_NOT_FOUND')}
</Typography>
</Box>
);
};

export async function getStaticProps({ locale }: any) {
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
// Will be passed to the page component as props
},
};
}

export default PageNotFound;
21 changes: 15 additions & 6 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import Head from 'next/head';
import IconButton from '@mui/material/IconButton';
import { Poppins } from 'next/font/google';
import { ToastContainer } from 'react-toastify';
import { appWithTranslation } from 'next-i18next';
import { UserConfig, appWithTranslation } from 'next-i18next';
import customTheme from '../styles/customTheme';
import { telemetryFactory } from '../utils/telemetry';
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { fullWidthPages } from '../../app.config';
import nextI18NextConfig from '../../next-i18next.config.js';

const queryClient = new QueryClient();
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
Expand All @@ -36,6 +38,13 @@ const poppins = Poppins({
subsets: ['latin'],
});

const emptyInitialI18NextConfig: UserConfig = {
i18n: {
defaultLocale: nextI18NextConfig.i18n.defaultLocale,
locales: nextI18NextConfig.i18n.locales,
},
};

export function DarkTheme() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
Expand All @@ -61,7 +70,7 @@ export function DarkTheme() {

function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const Login = router.pathname === '/login';
const isFullWidthPage = fullWidthPages.includes(router.pathname);
useEffect(() => {
telemetryFactory.init();
}, []);
Expand Down Expand Up @@ -136,12 +145,12 @@ function App({ Component, pageProps }: AppProps) {
sx={{
padding: '0',
'@media (min-width: 900px)': {
width: !Login ? 'calc(100% - 22rem)' : '100%',
marginLeft: !Login ? '351px' : '0',
width: !isFullWidthPage ? 'calc(100% - 22rem)' : '100%',
marginLeft: !isFullWidthPage ? '351px' : '0',
},
'@media (min-width: 1600px)': {
width: '100%',
marginLeft: !Login ? '351px' : '0',
marginLeft: !isFullWidthPage ? '351px' : '0',
},
}}
>
Expand All @@ -159,4 +168,4 @@ function App({ Component, pageProps }: AppProps) {
);
}

export default appWithTranslation(App);
export default appWithTranslation(App, emptyInitialI18NextConfig);
File renamed without changes.
3 changes: 2 additions & 1 deletion src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ const LoginPage = () => {

useEffect(() => {
if (typeof window !== 'undefined' && window.localStorage) {
let lang;
if (localStorage.getItem('preferredLanguage')) {
var lang = localStorage.getItem('preferredLanguage') || 'en';
lang = localStorage.getItem('preferredLanguage') || 'en';
} else {
lang = 'en';
}
Expand Down
57 changes: 57 additions & 0 deletions src/pages/unauthorized.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import { useTheme } from '@mui/material';

const Unauthorized = () => {
const { t } = useTranslation();
const theme = useTheme<any>();

return (
<Box
py={4}
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="center"
sx={{ height: '100vh' }} // '-webkit-fill-available' can be approximated with '100vh'
>
{/* <Image width={270} src={ErrorIcon} alt="Error icon" /> */}
<Typography
mt={4}
variant="h2"
fontSize="24px"
lineHeight="30px"
fontWeight="600"
color="black"
>
{t('COMMON.ACCESS_DENIED')}
</Typography>

<Typography
mt={4}
variant="subtitle2"
fontSize="16px"
lineHeight="16px"
fontWeight="600"
color={theme.palette.warning['400']}
>
{t('COMMON.YOU_DONT_HAVE_PERMISSION_TO_ACCESS_THIS_PAGE')}
</Typography>

</Box>
);
};

export async function getStaticProps({ locale }: any) {
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
// Will be passed to the page component as props
},
};
}

export default Unauthorized;
3 changes: 2 additions & 1 deletion src/store/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ const useStore = create(
persist(
(set) => ({
value: '',
role: '',
cohorts: [],
userRole: '',
pairs: [],
setValue: (newValue) => set((state) => ({ value: newValue })),
setUserRole: (newRole) => set((state) => ({ userRole: newRole })),
setCohorts: (newCohorts) => set(() => ({ cohorts: newCohorts })),
Expand Down
14 changes: 14 additions & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -946,3 +946,17 @@ main {
.text-7C {
color: var(--mui-palette-warning-400) !important;
}


/* To Hide the caret icon on input type number */
input[type="number"] {
-moz-appearance: textfield;
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
margin: 0;
-webkit-appearance: none;
}

/* To hide the input type number arrows */
9 changes: 9 additions & 0 deletions src/utils/Helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import FingerprintJS from 'fingerprintjs2';
import { Role, Status } from './app.constant';
import { i18n } from 'next-i18next';

export const ATTENDANCE_ENUM = {
PRESENT: 'present',
Expand Down Expand Up @@ -266,3 +267,11 @@ export const accessGranted = (
}
return false;
};


export function getTranslatedText(key: string, options?: any): string {
if (!i18n?.t) {
throw new Error('i18n instance is not initialized');
}
return i18n.t(key, options) as string;
}
5 changes: 5 additions & 0 deletions src/utils/hoc/withAccessControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ const withAccessControl =
const router = useRouter();

useEffect(() => {
console.log("userRole", userRole);
if (userRole === '') {
router.replace('/logout');
return;
}
if (!userRole || !accessControl[action]?.includes(userRole)) {
router.replace('/unauthorized');
}
Expand Down
Loading

0 comments on commit 7351c67

Please sign in to comment.