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) => (
+ -
+
+
+ ))}
+
+ >
+ );
+};
+
+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 (
+
+
+
+
+
+ {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';