diff --git a/actions/form.actions.ts b/actions/form.actions.ts new file mode 100644 index 00000000..5875e133 --- /dev/null +++ b/actions/form.actions.ts @@ -0,0 +1,23 @@ +'use server'; +import { getServerAuthSession } from '~/server/auth'; +import { db, forms } from '~/server/db'; + +export const getForms = async () => { + const session = await getServerAuthSession(); + if (!session?.person) return new Error('Not authenticated'); + return await db + .select({ + id: forms.id, + title: forms.title, + type: forms.type, + isActive: forms.isActive, + persistentUrl: forms.persistentUrl, + expiryDate: forms.expiryDate, + }) + .from(forms); + // .innerJoin( + // formsModifiableByPersons, + // eq(forms.id, formsModifiableByPersons.formId) + // ) + // .where(and(eq(formsVisibleToPersons.personId, session.person.id), eq(forms.isActive, true))); +}; diff --git a/app/[locale]/forms/client-utils.tsx b/app/[locale]/forms/client-utils.tsx new file mode 100644 index 00000000..b6b881b3 --- /dev/null +++ b/app/[locale]/forms/client-utils.tsx @@ -0,0 +1,136 @@ +'use client'; + +import { useSearchParams } from 'next/navigation'; +import { useDebounceCallback } from 'usehooks-ts'; + +import { + Input, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '~/components/inputs'; +import { Button } from '~/components/ui'; + +import { FormsCard } from './page'; + +export const Types = ({ types }: { types: string[] }) => { + const searchParams = useSearchParams(); + const query = searchParams.get('query') ?? ''; + const currentFormType = searchParams.get('type') ?? 'all'; + const formTypes = [ + 'all', + 'academic', + 'factulty feedback', + 'placement', + 'other', + ]; + + return ( + <> + +
    + {formTypes.map((name, index) => ( +
  1. + +
  2. + ))} +
+ + ); +}; + +export const SearchInput = ({ + defaultValue, + placeholder, +}: { + defaultValue?: string; + placeholder: string; +}) => { + const debounceCallback = useDebounceCallback( + (event: React.ChangeEvent) => { + window.history.replaceState(null, '', `?query=${event.target.value}`); + }, + 100 + ); + return ( + + ); +}; + +export const FormsList = async ({ + locale, + forms, + transText, +}: { + locale: string; + forms: { + id: number; + isActive: boolean; + type: 'academic' | 'all' | 'factulty feedback' | 'placement' | 'other'; + persistentUrl: string | null; + expiryDate: Date | null; + title: string; + }[]; + transText: { + active: string; + closed: string; + opened: string; + download: string; + }; +}) => { + const searchParams = useSearchParams(); + const query = searchParams.get('query') ?? ''; + const currentFormType = searchParams.get('type') ?? 'all'; + return forms + .filter( + (form) => + (form.title.includes(query) && 1) || + currentFormType === 'all' || + form.type === currentFormType + ) + .map((form) => ( + + )); +}; diff --git a/app/[locale]/forms/page.tsx b/app/[locale]/forms/page.tsx index 98634d15..a1bae200 100644 --- a/app/[locale]/forms/page.tsx +++ b/app/[locale]/forms/page.tsx @@ -1,18 +1,151 @@ -import Heading from '~/components/heading'; -import WorkInProgress from '~/components/work-in-progress'; +import Link from 'next/link'; +import { Suspense } from 'react'; +import { MdOutlineFileDownload } from 'react-icons/md'; + +import { getForms } from '~/actions/form.actions'; +import Loading from '~/components/loading'; +import { Button } from '~/components/ui'; import { getTranslations } from '~/i18n/translations'; +import { cn } from '~/lib/utils'; + +import { FormsList, SearchInput, Types } from './client-utils'; export default async function Forms({ params: { locale }, + searchParams: { types: formType, query }, }: { params: { locale: string }; + searchParams: { types?: string; query?: string }; }) { const text = (await getTranslations(locale)).Forms; - return ( - <> - ; - - +
+ + + + + + +
    + } key={`${query}-${formType}`}> + + +
+
); } + +const FormsDisplay = async ({ + locale, + loginPlease: { login, unauthorized }, + transText, +}: { + locale: string; + loginPlease: { login: string; unauthorized: string }; + transText: { + active: string; + closed: string; + opened: string; + download: string; + }; +}) => { + const formsList = await getForms(); + if (formsList instanceof Error) + return ( +
+

{unauthorized}

+ + + +
+ ); + return ( +
+ +
+ ); +}; + +export const FormsCard = ({ + form: { id, isActive, persistentUrl, expiryDate, title }, + transText: { active, closed, opened, download }, + locale, +}: { + form: { + id: number; + isActive: boolean; + type: 'academic' | 'all' | 'factulty feedback' | 'placement' | 'other'; + persistentUrl: string | null; + expiryDate: Date | null; + title: string; + }; + transText: { + active: string; + closed: string; + opened: string; + download: string; + }; + locale: string; +}) => { + return ( +
  • + + +
    + + + + +
    + {isActive ? active : closed} +
    +
    +

    + {opened}: + {expiryDate?.toLocaleDateString(locale)} +

    + +
    +

    {title}

    + +
  • + ); +}; diff --git a/i18n/en.ts b/i18n/en.ts index 6b0ef29e..0911d8d1 100644 --- a/i18n/en.ts +++ b/i18n/en.ts @@ -89,7 +89,19 @@ const text: Translations = { copyright: '© 2024 National Institute of Technology Kurukshetra. All Rights Reserved.', }, - Forms: { title: 'FORMS' }, + Forms: { + title: 'Forms', + placeholder: 'Search Academic, Feedback, Placements, etc...', + active: 'Active', + inactive: 'Closed', + opened: 'Opened', + download: 'Download', + loginPlease: { + unathorised: 'Not authenticated', + login: 'Login', + }, + types: ['All', 'Academic', 'Factulty Feedback', 'Placement', 'Other'], + }, Header: { institute: 'Institute', academics: 'Academics', diff --git a/i18n/hi.ts b/i18n/hi.ts index fa42a029..8ffccb39 100644 --- a/i18n/hi.ts +++ b/i18n/hi.ts @@ -85,7 +85,19 @@ const text: Translations = { copyright: '© २०२४ राष्ट्रीय प्रौद्योगिकी संस्थान कुरूक्षेत्र। सर्वाधिकार आरक्षित।', }, - Forms: { title: 'फॉर्म्स' }, + Forms: { + title: 'फॉर्म्स', + placeholder: 'शैक्षिक, प्रतिपुष्टि, नियुक्तियाँ, आदि खोजें...', + active: 'सक्रिय', + inactive: 'बंद', + opened: 'खुला', + download: 'डाउनलोड', + loginPlease: { + unathorised: 'अनधिकृत', + login: 'लॉग इन', + }, + types: ['शैक्षिक', 'तथ्यपरक प्रतिपुष्टि', 'नियुक्तियाँ', 'आदि'], + }, Header: { institute: 'संस्थान', academics: 'शैक्षिक', diff --git a/i18n/translations.ts b/i18n/translations.ts index e319c4d4..2f9b2060 100644 --- a/i18n/translations.ts +++ b/i18n/translations.ts @@ -77,7 +77,19 @@ export interface Translations { lorem: string; copyright: string; }; - Forms: { title: string }; + Forms: { + title: string; + placeholder: string; + types: string[]; + loginPlease: { + unathorised: string; + login: string; + }; + active: string; + inactive: string; + opened: string; + download: string; + }; Header: { institute: string; academics: string; diff --git a/server/db/schema/forms.schema.ts b/server/db/schema/forms.schema.ts index 7399e801..17cd92d9 100644 --- a/server/db/schema/forms.schema.ts +++ b/server/db/schema/forms.schema.ts @@ -12,6 +12,9 @@ import { export const forms = pgTable('forms', { id: serial('id').primaryKey(), title: varchar('title').notNull(), + type: varchar('type', { + enum: ['all', 'academic', 'factulty feedback', 'placement', 'other'], + }).notNull(), description: varchar('description').notNull(), visibleTo: smallint('visible_to') .array() diff --git a/server/db/schema/index.ts b/server/db/schema/index.ts index 3124a222..977d3ec7 100644 --- a/server/db/schema/index.ts +++ b/server/db/schema/index.ts @@ -11,6 +11,7 @@ export * from './departments.schema'; export * from './doctorates.schema'; export * from './faculty.schema'; export * from './faq.schema'; +export * from './forms.schema'; export * from './majors.schema'; export * from './notifications.schema'; export * from './persons.schema';