diff --git a/compass/app/auth/login/page.tsx b/compass/app/auth/login/page.tsx index ccb8b2c..9b50a2c 100644 --- a/compass/app/auth/login/page.tsx +++ b/compass/app/auth/login/page.tsx @@ -60,7 +60,7 @@ export default function Page() { {emailError && }
- +
{passwordError && } diff --git a/compass/utils/classes/Field.ts b/compass/utils/classes/Field.ts new file mode 100644 index 0000000..716846e --- /dev/null +++ b/compass/utils/classes/Field.ts @@ -0,0 +1,15 @@ +import { Icons } from "../constants"; + +export class Field { + iconKey: keyof typeof Icons; + title: string; + + constructor(iconKey: keyof typeof Icons, title: string) { + this.iconKey = iconKey; + this.title = title; +} + + validateInput(value: any): boolean { + return value !== null; + } +} diff --git a/compass/utils/classes/InputProps.ts b/compass/utils/classes/InputProps.ts deleted file mode 100644 index 2354d62..0000000 --- a/compass/utils/classes/InputProps.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { InputHTMLAttributes } from "react"; -import { Icons } from "../constants"; - -export type InputProps = InputHTMLAttributes & { - iconKey?: keyof typeof Icons; // Use keyof typeof to ensure the key exists in Icons - title?: string; // Assuming title is always a string - type?: string; - placeholder?: string; - }; \ No newline at end of file diff --git a/compass/utils/constants.tsx b/compass/utils/constants.ts similarity index 69% rename from compass/utils/constants.tsx rename to compass/utils/constants.ts index 2792d3a..cd9702a 100644 --- a/compass/utils/constants.tsx +++ b/compass/utils/constants.ts @@ -1,54 +1,58 @@ -import { ListBulletIcon, HashtagIcon, Bars3BottomLeftIcon, EnvelopeIcon, AtSymbolIcon, ClipboardIcon, ArrowsUpDownIcon, ChevronDoubleRightIcon, ChevronDoubleLeftIcon, ChevronRightIcon, ChevronLeftIcon, EyeIcon, EyeSlashIcon, UserIcon, BookOpenIcon, MagnifyingGlassIcon, LinkIcon } from '@heroicons/react/24/solid'; - -export const Icons = { - EmailInputIcon: EnvelopeIcon, - HidePasswordIcon: EyeSlashIcon, - UnhidePasswordIcon: EyeIcon, - UserIcon: UserIcon, - ResourceIcon: BookOpenIcon, - SearchIcon: MagnifyingGlassIcon, - ServiceIcon: ClipboardIcon, - CloseRightArrow: ChevronDoubleRightIcon, - CloseLeftArrow: ChevronDoubleLeftIcon, - LinkRightArrow:ChevronRightIcon, - LinkLeftArrow:ChevronLeftIcon, - SortIcon: ArrowsUpDownIcon, - EmailTableIcon:AtSymbolIcon, - LinkTableIcon: LinkIcon, - TextTableIcon: Bars3BottomLeftIcon, - NumberTableIcon: HashtagIcon, - MultiselectTableIcon: ListBulletIcon -}; - -export enum User { - ADMIN, - EMPLOYEE, - VOLUNTEER -} - -export enum COLLECTION { - RESOURCE, - SERVICE, - USER -} - -export enum PROGRAM { - DOMESTIC_VIOLENCE, - ECONOMIC_STABILITY, - COMMUNITY_EDUCATION -} - -export enum DATATYPE { - INTEGER, - STRING, - LINK, - EMAIL, - MULTISELECT, - SELECT -} - -// export const COLLECTION_MAP: {[key in COLLECTION]: CollectionImpl} = { -// [COLLECTION.RESOURCE]: new CollectionImpl('Resources', Icons.ResourceIcon), -// [COLLECTION.SERVICE]: new CollectionImpl('Services', Icons.ServiceIcon), -// [COLLECTION.USER]: new CollectionImpl('Users', Icons.UserIcon) -// } \ No newline at end of file +import { ListBulletIcon, HashtagIcon, Bars3BottomLeftIcon, EnvelopeIcon, AtSymbolIcon, ClipboardIcon, ArrowsUpDownIcon, ChevronDoubleRightIcon, ChevronDoubleLeftIcon, ChevronRightIcon, ChevronLeftIcon, EyeIcon, EyeSlashIcon, UserIcon, BookOpenIcon, MagnifyingGlassIcon, LinkIcon, ClipboardDocumentCheckIcon } from '@heroicons/react/24/solid'; + +export const Icons = { + EmailInputIcon: EnvelopeIcon, + HidePasswordIcon: EyeSlashIcon, + UnhidePasswordIcon: EyeIcon, + UserIcon: UserIcon, + ResourceIcon: BookOpenIcon, + SearchIcon: MagnifyingGlassIcon, + ServiceIcon: ClipboardIcon, + CloseRightArrow: ChevronDoubleRightIcon, + CloseLeftArrow: ChevronDoubleLeftIcon, + LinkRightArrow:ChevronRightIcon, + LinkLeftArrow:ChevronLeftIcon, + SortIcon: ArrowsUpDownIcon, + EmailTableIcon:AtSymbolIcon, + LinkTableIcon: LinkIcon, + TextTableIcon: Bars3BottomLeftIcon, + NumberTableIcon: HashtagIcon, + MultiselectTableIcon: ListBulletIcon, + RequirementsTableIcon: ClipboardDocumentCheckIcon +}; + +export enum USER { + ADMIN, + EMPLOYEE, + VOLUNTEER +} + +export enum COLLECTION { + RESOURCE, + SERVICE, + USER, + TRAINING_MANUAL +} + +export enum PROGRAM { + DOMESTIC_VIOLENCE, + ECONOMIC_STABILITY, + COMMUNITY_EDUCATION +} + +export enum STATUS { + FULL, + CLOSED, + ACCEPTING_CLIENTS +} + +export enum DATATYPE { + INTEGER, + STRING, + LINK, + EMAIL, + MULTISELECT, + SELECT +} + + diff --git a/compass/utils/functions/mockFetch.ts b/compass/utils/functions/mockFetch.ts new file mode 100644 index 0000000..6f79147 --- /dev/null +++ b/compass/utils/functions/mockFetch.ts @@ -0,0 +1,137 @@ +import { PROGRAM, STATUS, USER } from "../constants"; + +const serviceEntries = [ + { + name: "Empowerment Workshops", + status: [STATUS.ACCEPTING_CLIENTS], + summary: "Workshops to empower victims through education and skill-building.", + requirements: "Resident of the community and victim of domestic violence.", + program: [PROGRAM.DOMESTIC_VIOLENCE, PROGRAM.COMMUNITY_EDUCATION], + tags: ["empowerment", "education"], + }, + { + name: "Financial Literacy Courses", + status: [STATUS.ACCEPTING_CLIENTS, STATUS.FULL], + summary: "Courses aimed at improving financial independence for victims.", + requirements: "Open to all domestic violence victims.", + program: [PROGRAM.ECONOMIC_STABILITY], + tags: ["finance", "literacy"], + }, + { + name: "Counseling Services", + status: [STATUS.ACCEPTING_CLIENTS], + summary: "Professional counseling for individuals and families affected by domestic violence.", + requirements: "Appointment required.", + program: [PROGRAM.DOMESTIC_VIOLENCE], + tags: ["counseling", "mental health"], + }, + { + name: "Job Placement Program", + status: [STATUS.ACCEPTING_CLIENTS], + summary: "Assistance with job search and placement for survivors.", + requirements: "Must be actively seeking employment.", + program: [PROGRAM.ECONOMIC_STABILITY], + tags: ["job", "employment"], + }, + { + name: "Legal Advocacy", + status: [STATUS.FULL], + summary: "Legal advice and representation for victims of domestic violence.", + requirements: "Legal documentation of domestic violence required.", + program: [PROGRAM.DOMESTIC_VIOLENCE], + tags: ["legal", "advocacy"], + } + ]; + +const resourceEntries = [ + { + name: "Legal Aid Reference", + summary: "Comprehensive list of legal resources for victims.", + link: "https://legalaid.example.com", + program: [PROGRAM.DOMESTIC_VIOLENCE], + tags: ["legal", "aid"], + }, + { + name: "Shelter Locations", + summary: "Directory of safe shelters for victims escaping abuse.", + link: "https://shelters.example.com", + program: [PROGRAM.DOMESTIC_VIOLENCE], + tags: ["shelter", "safety"], + }, + { + name: "Support Group Finder", + summary: "Find local support groups for survivors of domestic violence.", + link: "https://supportgroups.example.com", + program: [PROGRAM.COMMUNITY_EDUCATION], + tags: ["support", "community"], + }, + { + name: "Employment Services", + summary: "Resources for job training and placement services.", + link: "https://employment.example.com", + program: [PROGRAM.ECONOMIC_STABILITY], + tags: ["job", "training"], + }, + { + name: "Educational Workshops", + summary: "Schedule of educational workshops on various topics.", + link: "https://workshops.example.com", + program: [PROGRAM.COMMUNITY_EDUCATION], + tags: ["education", "workshops"], + } + ]; + +const userEntries = [ + { + name: "Alex Johnson", + role: [USER.VOLUNTEER], + email: "alex.johnson@example.com", + program: [PROGRAM.DOMESTIC_VIOLENCE], + experience: 2, + group: "Volunteer Group A", + }, + { + name: "Sam Lee", + role: [USER.EMPLOYEE], + email: "sam.lee@example.com", + program: [PROGRAM.ECONOMIC_STABILITY], + experience: 5, + group: "Economic Support Team", + }, + { + name: "Jordan Smith", + role: [USER.ADMIN, USER.VOLUNTEER], + email: "jordan.smith@example.com", + program: [PROGRAM.COMMUNITY_EDUCATION, PROGRAM.DOMESTIC_VIOLENCE], + experience: 3, + group: "Outreach and Education", + }, + { + name: "Casey Martinez", + role: [USER.VOLUNTEER], + email: "casey.martinez@example.com", + program: [PROGRAM.ECONOMIC_STABILITY], + experience: 1, + group: "Financial Literacy Volunteers", + }, + { + name: "Jamie Chung", + role: [USER.EMPLOYEE], + email: "jamie.chung@example.com", + program: [PROGRAM.DOMESTIC_VIOLENCE], + experience: 4, + group: "Counseling Services Team", + } + ]; + +export const mockFetchServices = () => { + return serviceEntries; +} + +export const mockFetchResources = () => { + return resourceEntries; +} + +export const mockFetchUsers = () => { + return userEntries; +} \ No newline at end of file diff --git a/compass/utils/implementations/CollectionDataImpl.ts b/compass/utils/implementations/CollectionDataImpl.ts new file mode 100644 index 0000000..6e0ef92 --- /dev/null +++ b/compass/utils/implementations/CollectionDataImpl.ts @@ -0,0 +1,39 @@ +import { Field } from "../classes/Field"; + +export class CollectionDataImpl { + headers: Field[]; + rows: Record[]; + + constructor(headers: Field[] = [], rows: Record[] = []) { + this.headers = headers; + this.rows = rows; + } + + addRow(row: Record): void { + let isValidRow = true; + + for (const header of this.headers) { + const value = row[header.title]; + if (!header.validateInput(value)) { + console.error(`Validation failed for ${header.title} with value ${value}`); + isValidRow = false; + break; + } + } + + if (isValidRow) { + this.rows.push(row); + } else { + console.log('Row not added due to validation failure.'); + } + } + + getRows(): Record[] { + return this.rows; + } + + getHeaders(): Field[] { + return this.headers; + } + +} \ No newline at end of file diff --git a/compass/utils/implementations/CollectionImpl.ts b/compass/utils/implementations/CollectionImpl.ts new file mode 100644 index 0000000..a940ac9 --- /dev/null +++ b/compass/utils/implementations/CollectionImpl.ts @@ -0,0 +1,15 @@ +import { CollectionDataImpl } from "./CollectionDataImpl"; + +export class CollectionImpl { + title: string; + icon: any; + data: CollectionDataImpl; + + constructor(title: string, icon: any, data: CollectionDataImpl) { + this.title = title; + this.icon = icon; + this.data = data; + } + + +} \ No newline at end of file diff --git a/compass/utils/implementations/FieldImpl/EmailFieldImpl.ts b/compass/utils/implementations/FieldImpl/EmailFieldImpl.ts new file mode 100644 index 0000000..bb2640a --- /dev/null +++ b/compass/utils/implementations/FieldImpl/EmailFieldImpl.ts @@ -0,0 +1,18 @@ +import { Field } from "@/utils/classes/Field"; + + +export class EmailFieldImpl extends Field { + + constructor() { + super('EmailTableIcon', "Email"); + } + + validateInput(value: any) : boolean { + if (typeof value !== 'string') { + return false; + } + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(value); + } + +} \ No newline at end of file diff --git a/compass/utils/implementations/FieldImpl/IntegerFieldImpl.ts b/compass/utils/implementations/FieldImpl/IntegerFieldImpl.ts new file mode 100644 index 0000000..f5b7dc1 --- /dev/null +++ b/compass/utils/implementations/FieldImpl/IntegerFieldImpl.ts @@ -0,0 +1,15 @@ +import { Icons } from "@/utils/constants"; +import { Field } from "@/utils/classes/Field"; + + +export class IntegerFieldImpl extends Field { + + constructor(title: string) { + super('NumberTableIcon', title); + } + + validateInput(value: any) : boolean { + return Number.isInteger(value); + } + +} \ No newline at end of file diff --git a/compass/utils/implementations/FieldImpl/LinkFieldImpl.ts b/compass/utils/implementations/FieldImpl/LinkFieldImpl.ts new file mode 100644 index 0000000..b091293 --- /dev/null +++ b/compass/utils/implementations/FieldImpl/LinkFieldImpl.ts @@ -0,0 +1,18 @@ +import { Field } from "@/utils/classes/Field"; + + +export class LinkFieldImpl extends Field { + + constructor() { + super('LinkTableIcon', "Link"); + } + + validateInput(value: any) : boolean { + if (typeof value !== 'string') { + return false; + } + const urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/; + return urlRegex.test(value); + } + +} \ No newline at end of file diff --git a/compass/utils/implementations/FieldImpl/MultiselectFieldImpl.ts b/compass/utils/implementations/FieldImpl/MultiselectFieldImpl.ts new file mode 100644 index 0000000..478dcf0 --- /dev/null +++ b/compass/utils/implementations/FieldImpl/MultiselectFieldImpl.ts @@ -0,0 +1,39 @@ +import { Field } from "@/utils/classes/Field"; + + +export class MultiselectFieldImpl extends Field { + + tags: Set; + selectedTags: Set; + + constructor(title: string, tags: Set = new Set()) { + super('MultiselectTableIcon', title); + this.tags = tags + this.selectedTags = new Set(); + } + + getTags() { + return this.tags + } + + addTag(tag: any) { + this.tags.add(tag); + } + + removeTag(tag: any) { + if (this.tags.has(tag)){ + this.tags.delete(tag); + } + } + + selectTag(tag: any) { + this.selectedTags.add(tag); + } + + removeSelectedTag(tag: any) { + if (this.selectedTags.has(tag)){ + this.selectedTags.delete(tag); + } + } + +} \ No newline at end of file diff --git a/compass/utils/implementations/FieldImpl/StringFieldImpl.ts b/compass/utils/implementations/FieldImpl/StringFieldImpl.ts new file mode 100644 index 0000000..49d0538 --- /dev/null +++ b/compass/utils/implementations/FieldImpl/StringFieldImpl.ts @@ -0,0 +1,15 @@ +import { Icons } from "@/utils/constants"; +import { Field } from "@/utils/classes/Field"; + + +export class StringFieldImpl extends Field { + + constructor(title: string, iconKey: keyof typeof Icons = "TextTableIcon") { + super(iconKey, title); + } + + validateInput(value: any) : boolean { + return typeof value === 'string'; + } + +} \ No newline at end of file diff --git a/compass/utils/models/Collection.ts b/compass/utils/models/Collection.ts new file mode 100644 index 0000000..7f581c0 --- /dev/null +++ b/compass/utils/models/Collection.ts @@ -0,0 +1,15 @@ +import { mockFetchResources, mockFetchServices, mockFetchUsers } from "../functions/mockFetch"; +import { CollectionDataImpl } from "../implementations/CollectionDataImpl"; +import { CollectionImpl } from "../implementations/CollectionImpl"; +import { ResourceCollectionDataType, ServiceCollectionDataType, UserCollectionDataType } from "./CollectionDataType"; + +const ServiceCollectionData = new CollectionDataImpl(ServiceCollectionDataType, mockFetchServices()); +const ResourceCollectionData = new CollectionDataImpl(ResourceCollectionDataType, mockFetchResources()); +const UserCollectionData = new CollectionDataImpl(UserCollectionDataType, mockFetchUsers()); + +export const ServiceCollection = new CollectionImpl('Service','ServiceIcon',new CollectionDataImpl(ServiceCollectionDataType)) + +export const ResourceCollection = new CollectionImpl('Resource','ResourceIcon',new CollectionDataImpl(ResourceCollectionDataType)) + +export const UserCollection = new CollectionImpl('User','UserIcon',new CollectionDataImpl(UserCollectionDataType)) + diff --git a/compass/utils/models/CollectionDataType.ts b/compass/utils/models/CollectionDataType.ts new file mode 100644 index 0000000..883522c --- /dev/null +++ b/compass/utils/models/CollectionDataType.ts @@ -0,0 +1,26 @@ +import { Field } from "../classes/Field"; +import { PROGRAM, STATUS, USER } from "../constants"; +import { EmailFieldImpl } from "../implementations/FieldImpl/EmailFieldImpl"; +import { IntegerFieldImpl } from "../implementations/FieldImpl/IntegerFieldImpl"; +import { LinkFieldImpl } from "../implementations/FieldImpl/LinkFieldImpl"; +import { MultiselectFieldImpl } from "../implementations/FieldImpl/MultiselectFieldImpl"; +import { StringFieldImpl } from "../implementations/FieldImpl/StringFieldImpl"; + +const programSet: Set = new Set([PROGRAM.COMMUNITY_EDUCATION, PROGRAM.DOMESTIC_VIOLENCE, PROGRAM.ECONOMIC_STABILITY]); +const program = new MultiselectFieldImpl("program", programSet); +const requirements = new StringFieldImpl("requirements","RequirementsTableIcon"); +const name = new StringFieldImpl("name"); +const summary = new StringFieldImpl("summary"); +const statusSet: Set = new Set([STATUS.ACCEPTING_CLIENTS, STATUS.CLOSED, STATUS.FULL]) +const status = new MultiselectFieldImpl("status", statusSet); +const link = new LinkFieldImpl() +const tags = new MultiselectFieldImpl("tags") +const roleSet: Set = new Set([USER.ADMIN, USER.EMPLOYEE, USER.VOLUNTEER]); +const role = new MultiselectFieldImpl("role", roleSet) +const experience = new IntegerFieldImpl("yoe") +const email = new EmailFieldImpl() +const group = new StringFieldImpl("group") + +export const ServiceCollectionDataType: Field[] = [name, status, summary, requirements, program, tags] +export const ResourceCollectionDataType: Field[] = [name, summary, link, program, tags] +export const UserCollectionDataType: Field[] = [name, role, email, program, experience, group] \ No newline at end of file