Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KB-225-Block-campus-functionality-if-student-not-signed-Code-of-Honor #355

120 changes: 120 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-alert-dialog": "^1.1.5",
"@radix-ui/react-aspect-ratio": "^1.1.0",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.2",
Expand Down
3 changes: 2 additions & 1 deletion src/actions/profile.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Contact, ContactType } from '@/types/contact';
import { campusFetch } from '@/lib/client';
import { revalidatePath } from 'next/cache';
import { getUserDetails } from '@/actions/auth.actions';
import { redirect } from 'next/navigation';

export async function getContacts() {
try {
Expand Down Expand Up @@ -101,8 +102,8 @@ export async function acceptCodeOfHonor() {
await campusFetch('profile/code-of-honor', {
method: 'PUT',
});
return getUserDetails();
} catch (error) {
throw new Error('Error while accepting code of honor');
}
redirect('/');
}
45 changes: 45 additions & 0 deletions src/app/[locale]/(private)/accept-code-of-honor/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client';

import { useTranslations } from 'next-intl';
import React from 'react';
import { acceptCodeOfHonor } from '@/actions/profile.actions';
import {
AlertDialog,
AlertDialogAction,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Link } from '@/i18n/routing';
import { Paragraph } from '@/components/typography/paragraph';

export default function CodeOfHonorAlert() {
const t = useTranslations('private.profile');

const handleAcceptCodeOfHonor = async () => {
await acceptCodeOfHonor();
};

return (
<AlertDialog defaultOpen={true}>
<AlertDialogContent className="w-[340px] rounded-[12px] sm:w-full" closable={false}>
<AlertDialogHeader>
<AlertDialogTitle>{t('codeOfHonor.title')}</AlertDialogTitle>
<AlertDialogDescription>
{t.rich('codeOfHonor.content', {
documentsLink: (chunks) => <Link href="/kpi-documents">{chunks}</Link>,
paragraph: (chunks) => <Paragraph className="m-0">{chunks}</Paragraph>,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogAction className="w-full md:w-fit" onClick={handleAcceptCodeOfHonor}>
{t('button.agree')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
42 changes: 3 additions & 39 deletions src/app/[locale]/(private)/profile/components/code-of-honor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,15 @@
import { Heading6 } from '@/components/typography/headers';
import { Separator } from '@/components/ui/separator';
import { Paragraph } from '@/components/typography/paragraph';
import { Button } from '@/components/ui/button';
import { useIsMobile } from '@/hooks/use-mobile';
import { useLocalStorage } from '@/hooks/use-storage';
import { User } from '@/types/user';
import { acceptCodeOfHonor } from '@/actions/profile.actions';
import React, { useState } from 'react';
import React from 'react';
import { useTranslations } from 'next-intl';
import { Check } from '@/app/images';
import { useServerErrorToast } from '@/hooks/use-server-error-toast';
import { Link } from '@/i18n/routing';

export function CodeOfHonor() {
const { errorToast } = useServerErrorToast();

const isMobile = useIsMobile();

const [user, setUser] = useLocalStorage<User>('user');

const [loading, setLoading] = useState(false);

const t = useTranslations('private.profile');

const handleAcceptCodeOfHonor = async () => {
setLoading(true);
const res = await acceptCodeOfHonor();
setLoading(false);

if (!res) {
errorToast();
return;
}
setUser(res);
};

const [user] = useLocalStorage<User>('user');
return (
<div className="flex flex-col gap-3">
<Heading6>{t('codeOfHonor.title')}</Heading6>
Expand All @@ -45,22 +20,11 @@ export function CodeOfHonor() {
documentsLink: (chunks) => <Link href="/kpi-documents">{chunks}</Link>,
paragraph: (chunks) => <Paragraph className="m-0 text-lg">{chunks}</Paragraph>,
})}
{user?.codeOfHonorSignDate ? (
{user?.codeOfHonorSignDate && (
<div className="flex flex-col gap-1">
<Paragraph>{t('codeOfHonor.agreement')}</Paragraph>
<Paragraph className="m-0">{user?.codeOfHonorSignDate}</Paragraph>
</div>
) : (
<Button
className="ml-auto w-fit"
loading={loading}
onClick={handleAcceptCodeOfHonor}
size={isMobile ? 'medium' : 'big'}
icon={<Check />}
iconPosition="end"
>
{t('button.agree')}
</Button>
)}
</div>
);
Expand Down
44 changes: 41 additions & 3 deletions src/app/[locale]/(private)/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,56 @@
import { getContacts, getContactTypes } from '@/actions/profile.actions';
import { Profile } from '@/app/[locale]/(private)/profile/profile';
import { getTranslations } from 'next-intl/server';
import { SubLayout } from '@/app/[locale]/(private)/sub-layout';
import { getUserDetails } from '@/actions/auth.actions';
import { Heading1 } from '@/components/typography/headers';
import { Paragraph } from '@/components/typography/paragraph';
import { InfoBlock } from '@/app/[locale]/(private)/profile/components/info-block';
import { Card, CardContent } from '@/components/ui/card';
import { Contacts } from '@/app/[locale]/(private)/profile/components/contacts';
import { Show } from '@/components/utils/show';
import { IntellectAgreement } from '@/app/[locale]/(private)/profile/components/intellect-agreement';
import { IntellectPublicationInfo } from '@/app/[locale]/(private)/profile/components/intellect-publication-info';
import { CodeOfHonor } from '@/app/[locale]/(private)/profile/components/code-of-honor';

const INTL_NAMESPACE = 'private.profile';

export async function generateMetadata() {
const t = await getTranslations({ namespace: 'private.profile' });
const t = await getTranslations(INTL_NAMESPACE);

return {
title: t('title'),
};
}

export default async function Page() {
const t = await getTranslations(INTL_NAMESPACE);

const user = await getUserDetails();

const contacts = await getContacts();
const contactTypes = await getContactTypes();

return <Profile contacts={contacts} contactTypes={contactTypes} />;
const isEmployee = !!user?.employeeProfile;

return (
<SubLayout pageTitle={t('title')}>
<div className="col-span-12">
<Heading1>{t('title')}</Heading1>
<Paragraph className="text-neutral-700">{t('subtitle')}</Paragraph>
<div className="flex flex-col gap-5 xl:flex-row">
<InfoBlock className="h-fit w-full" />
<Card className="h-fit w-full">
<CardContent className="flex flex-col gap-6 space-y-1.5 p-9">
<Contacts contacts={contacts} contactTypes={contactTypes} />
<Show when={isEmployee}>
<IntellectAgreement />
<IntellectPublicationInfo />
</Show>
<CodeOfHonor />
</CardContent>
</Card>
</div>
</div>
</SubLayout>
);
}
1 change: 1 addition & 0 deletions src/app/[locale]/(private)/sub-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { cn } from '@/lib/utils';
import React from 'react';
import { useTranslations } from 'next-intl';

interface SubLayoutProps {
Expand Down
16 changes: 11 additions & 5 deletions src/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ const AlertDialogOverlay = React.forwardRef<
));
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;

interface AlertDialogContentProps extends React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> {
closable?: boolean;
}

const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
AlertDialogContentProps
>(({ className, closable = true, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
Expand All @@ -39,9 +43,11 @@ const AlertDialogContent = React.forwardRef<
)}
{...props}
>
<AlertDialogPrimitive.Cancel asChild className="absolute right-3 top-3">
<Button className="text-gray-600 focus:outline-none" variant="tertiary" icon={<X />} />
</AlertDialogPrimitive.Cancel>
{closable && (
<AlertDialogPrimitive.Cancel asChild className="absolute right-3 top-3">
<Button className="text-gray-600 focus:outline-none" variant="tertiary" icon={<X />} />
</AlertDialogPrimitive.Cancel>
)}
{props.children}
</AlertDialogPrimitive.Content>
</AlertDialogPortal>
Expand Down
Loading
Loading