diff --git a/app/(API Routes)/api/blog/search/route.ts b/app/(API Routes)/api/blog/search/route.ts
index 8113116..b7affa1 100644
--- a/app/(API Routes)/api/blog/search/route.ts
+++ b/app/(API Routes)/api/blog/search/route.ts
@@ -7,7 +7,6 @@ export function GET(request: Request) {
const dataFile = path.join(
process.cwd(),
- "src",
"app",
"(API Routes)",
"api",
diff --git a/app/(API Routes)/api/search/[state]/route.ts b/app/(API Routes)/api/search/[state]/route.ts
new file mode 100644
index 0000000..8803252
--- /dev/null
+++ b/app/(API Routes)/api/search/[state]/route.ts
@@ -0,0 +1 @@
+// Dynamic State resource route
\ No newline at end of file
diff --git a/app/(API Routes)/api/search/resources.json b/app/(API Routes)/api/search/resources.json
new file mode 100644
index 0000000..e26fa05
--- /dev/null
+++ b/app/(API Routes)/api/search/resources.json
@@ -0,0 +1,50 @@
+[
+ {
+ "id": 1,
+ "url": "https://findtreatment.gov/",
+ "description": "Find treatment facilities in the United States or U.S. Territories for substance abuse/addiction and/or mental health problems."
+ },
+ {
+ "id": 2,
+ "url": "www.careeronestop.org/",
+ "description": "Help finding second chance employers by location"
+ },
+ {
+ "id": 3,
+ "url": "www.hudexchange.info/",
+ "description": "Housing and homeless shelters by location"
+ },
+ {
+ "id": 4,
+ "url": "www.hirefelons.org/",
+ "description": "Hiring resources for felons"
+ },
+ {
+ "id": 5,
+ "url": "https://www.spl.org/programs-and-services/civics-and-social-services/resources-for-the-formerly-incarcerated",
+ "description": "Information about resources available to the formerly incarcerated"
+ },
+ {
+ "id": 6,
+ "url": "www.goodwill.org/",
+ "description": "Goodwill has re entry jobs and training services"
+ },
+ { "id": 7, "url": "www.salvationarmyusa.org/", "description": "Resources" },
+ { "id": 8, "url": "www.foodfinder.us/", "description": "Resources" },
+ {
+ "id": 9,
+ "url": "https://georeentryconnect.com/",
+ "description": "General resources by state"
+ },
+ {
+ "id": 10,
+ "url": "www.honestjobs.com/",
+ "description": "Job seekers center for community transitions"
+ },
+ {
+ "id": 11,
+ "url": "https://centerforcommunitytransitions.org/",
+ "description": "Center for Community Transitions"
+ },
+ { "id": 12, "url": "www.indeed.com/", "description": "Job search" }
+]
diff --git a/app/(API Routes)/api/search/route.ts b/app/(API Routes)/api/search/route.ts
new file mode 100644
index 0000000..2df45d3
--- /dev/null
+++ b/app/(API Routes)/api/search/route.ts
@@ -0,0 +1,8 @@
+// Catch all search route
+// Import the resources file
+/*
+UPDATE resource file via vercel blob storage for source of truth
+USING os path to dynamically read files from vercel blob storage
+*/
+//
+//
diff --git a/app/(Pages)/about-us/page.tsx b/app/(Pages)/about/page.tsx
similarity index 96%
rename from app/(Pages)/about-us/page.tsx
rename to app/(Pages)/about/page.tsx
index b32a5e7..ac18119 100644
--- a/app/(Pages)/about-us/page.tsx
+++ b/app/(Pages)/about/page.tsx
@@ -1,7 +1,7 @@
"use client"
import NextImage from 'next/image';
import TeamMemberCard from '../../../components/about/team-member-card';
-import { GemIcon } from '@/components/ui/icons';
+// import { GemIcon } from '@/components/ui/icons';
import { executiveBoard, teamMembers } from '../../../data/team';
import {
Container, Box, Text, AbsoluteCenter, VStack, HStack, Heading,
@@ -10,6 +10,7 @@ import {
import { poppins } from '../../../components/ui/fonts';
import NextLink from 'next/link';
import checkDeviceSize from '../../../components/ui/breakpoints';
+import { Icon } from '@/components/ui/icons/icon';
export default function AboutUs() {
@@ -58,7 +59,8 @@ export default function AboutUs() {
-
+
+ {/* */}
Vision
@@ -70,7 +72,7 @@ export default function AboutUs() {
-
+
Mission
diff --git a/app/(Pages)/contact/page.tsx b/app/(Pages)/contact/page.tsx
index 5851574..03487d9 100644
--- a/app/(Pages)/contact/page.tsx
+++ b/app/(Pages)/contact/page.tsx
@@ -1,213 +1,207 @@
-"use client"
-import Image from 'next/image';
-import ContactForm from '../../../components/contact/contact-form';
-import { contactInfo, socialLinks, mapEmbedUrl } from '../../../data/contact';
+"use client";
-import {
- Container, Box, Text, AbsoluteCenter, VStack, HStack, Heading,
- Link as ChakraLink, SimpleGrid, Image as ChakraImage
-} from '@chakra-ui/react';
-import { poppins } from '../../../components/ui/fonts';
+import Image from "next/image";
+import ContactForm from "../../../components/contact/contact-form";
+import { contactInfo, socialLinks, mapEmbedUrl } from "../../../data/contact";
-import checkDeviceSize from '@/components/ui/breakpoints';
-import { HeaderTemplate, PageBuilder } from '../../../components/page-builder/template';
+import NextLink from "next/link";
+import {
+ Box,
+ Text,
+ HStack,
+ Heading,
+ Link as ChakraLink,
+ Image as ChakraImage,
+} from "@chakra-ui/react";
+
+import checkDeviceSize from "@/components/ui/breakpoints";
+import {
+ HeaderTemplate,
+ PageBuilder,
+} from "@/components/page-builder/template";
+import { Icon } from "@/components/ui/icons/icon";
export default function Contact() {
const notMobileDevice = checkDeviceSize();
return (
<>
-
- {
- notMobileDevice ? (
-
-
-
-
-
-
-
- {/* Get In Touch Section */}
-
-
-
Get in Touch
-
- We're here to support you. Reach out to learn more about our services, volunteer opportunities, and ways to contribute
-
-
-
-
-
- {/* Contact Info Cards Section */}
-
-
- {contactInfo.map((info) => {
- const content = info.href ? (
-
- {info.value}
-
- ) : (
-
{info.value}
- );
-
- return (
-
-
- {info.icon}
-
- {info.label}
- {content}
-
- );
- })}
-
-
-
- {/* Google Maps Embed Section */}
-
-
+ {notMobileDevice ? (
+
+
+
+
+
Get in Touch
+
+ We're here to support you. Reach out to learn more about our
+ services, volunteer opportunities, and ways to contribute
+
+
+
+ {socialLinks.map((link) => (
+
+
+
+ {link.platform}
+
+
+ ))}
+
+ {/* Contact Info Cards Section */}
+
+
+ {contactInfo.map((info) => {
+ const content = info.href ? (
+
{info.value}
+ ) : (
+
{info.value}
+ );
+
+ return (
+
+ {info.icon}
+ {info.label}
+ {content}
+
+ );
+ })}
+
+
+
+ {/* Google Maps Embed Section */}
+
+
+
+
{/* Contact Form Section */}
-
-
-
- ) : (
- <>
-
-
-
- {/* Hero Section */}
-
+
+
+
+
+ ) : (
+ <>
+
+ {/* Hero Section */}
+
+
+
+
+
Contact Us
+
+ Find out how you can contribute and make a positive impact in
+ your community
+
+
+
+
+ {/* Get In Touch Section */}
+
+
+
Get in Touch
+
+ We're here to support you. Reach out to learn more about
+ our services, volunteer opportunities, and ways to contribute
+
-
Contact Us
-
Find out how you can contribute and make a positive impact in your community
-
-
-
- {/* Get In Touch Section */}
-
-
-
Get in Touch
-
- We're here to support you. Reach out to learn more about our services, volunteer opportunities, and ways to contribute
-
-
-
-
-
- {/* Contact Info Cards Section */}
-
-
- {contactInfo.map((info) => {
- const content = info.href ? (
-
- {info.value}
-
- ) : (
-
{info.value}
- );
-
- return (
-
-
- {info.icon}
-
- {info.label}
- {content}
-
- );
- })}
+ {socialLinks.map((link) => (
+
+
+ {link.platform}
+
+ ))}
-
-
- {/* Google Maps Embed Section */}
-
-
- {/* Contact Form Section */}
-
- )
- >
- )
- }
+
+
+
+ {/* Contact Info Cards Section */}
+
+
+ {contactInfo.map((info) => {
+ const content = info.href ? (
+
{info.value}
+ ) : (
+
{info.value}
+ );
+
+ return (
+
+ {info.icon}
+ {info.label}
+ {content}
+
+ );
+ })}
+
+
+
+ {/* Google Maps Embed Section */}
+
+
+ {/* Contact Form Section */}
+
+
+ )
+ >
+ )}
>
);
}
diff --git a/app/(Pages)/search/[mock-blog]/blogs.json b/app/(Pages)/resources/[mock-blog]/blogs.json
similarity index 100%
rename from app/(Pages)/search/[mock-blog]/blogs.json
rename to app/(Pages)/resources/[mock-blog]/blogs.json
diff --git a/app/(Pages)/search/[mock-blog]/page.tsx b/app/(Pages)/resources/[mock-blog]/page.tsx
similarity index 100%
rename from app/(Pages)/search/[mock-blog]/page.tsx
rename to app/(Pages)/resources/[mock-blog]/page.tsx
diff --git a/app/(Pages)/resources/mock.json b/app/(Pages)/resources/mock.json
new file mode 100644
index 0000000..7b20df2
--- /dev/null
+++ b/app/(Pages)/resources/mock.json
@@ -0,0 +1,100 @@
+{
+ "items": [
+ {
+ "id": 1,
+ "title": "FindTreatment.gov",
+ "url": "https://findtreatment.gov/",
+ "description": "Find treatment facilities in the United States or U.S. Territories for substance abuse/addiction and/or mental health problems.",
+ "region": ["Georgia", "Wisconsin"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 2,
+ "title": "CareerOneStop.org",
+ "url": "https://www.careeronestop.org/",
+ "description": "Help finding second chance employers by location",
+ "region": ["Alabama", "California"],
+ "category": ["Hiring Platform", "Second Chance"]
+ },
+ {
+ "id": 3,
+ "title": "HUD Exchange",
+ "url": "https://www.hudexchange.info/",
+ "description": "Housing and homeless shelters by location",
+ "region": ["Louisiana", "Arkansas"],
+ "category": ["Housing"]
+ },
+ {
+ "id": 4,
+ "title": "HireFelons.org",
+ "url": "https://www.hirefelons.org/",
+ "description": "Hiring resources for felons",
+ "region": ["State", "Second State"],
+ "category": ["Hiring Platform", "Second Chance"]
+ },
+ {
+ "id": 5,
+ "title": "SPL Resources for the Formerly Incarcerated",
+ "url": "https://www.spl.org/programs-and-services/civics-and-social-services/resources-for-the-formerly-incarcerated",
+ "description": "Information about resources available to the formerly incarcerated",
+ "region": ["State", "Second State"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 6,
+ "title": "Goodwill Industries International",
+ "url": "https://www.goodwill.org/",
+ "description": "Goodwill has re entry jobs and training services",
+ "region": ["State", "Second State"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 7,
+ "title": "Salvation Army Resources",
+ "url": "https://www.salvationarmyusa.org/",
+ "description": "Resources",
+ "region": ["State", "Second State"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 8,
+ "title": "Food Finder",
+ "url": "https://www.foodfinder.us/",
+ "description": "Resources",
+ "region": ["State", "Second State"],
+ "category": ["Food Assistance"]
+ },
+ {
+ "id": 9,
+ "title": "GeoReentryConnect",
+ "url": "https://georeentryconnect.com/",
+ "description": "General resources by state",
+ "region": ["State", "Second State"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 10,
+ "title": "Honest Jobs",
+ "url": "https://www.honestjobs.com/",
+ "description": "Job seekers center for community transitions",
+ "region": ["State", "Second State"],
+ "category": ["Hiring Platform", "Second Chance"]
+ },
+ {
+ "id": 11,
+ "title": "Center for Community Transitions",
+ "url": "https://centerforcommunitytransitions.org/",
+ "description": "Center for Community Transitions",
+ "region": ["State", "Second State"],
+ "category": ["Healthcare"]
+ },
+ {
+ "id": 12,
+ "title": "Indeed.com",
+ "url": "https://www.indeed.com/",
+ "description": "Job search",
+ "region": ["State", "Second State"],
+ "category": ["Hiring Platform"]
+ }
+ ]
+}
diff --git a/app/(Pages)/resources/mockIndex.ts b/app/(Pages)/resources/mockIndex.ts
new file mode 100644
index 0000000..8fe97bd
--- /dev/null
+++ b/app/(Pages)/resources/mockIndex.ts
@@ -0,0 +1,27 @@
+import ResourceList from "./mock.json";
+
+type ResourceItem = {
+ id: number;
+ title: string;
+ description: string;
+ category: string[];
+ region: string[];
+ url: string;
+};
+type ResourceFile = { items: ResourceItem[] };
+// Strongly types the JSON import
+const Resources = ResourceList as ResourceFile;
+// Export all items export
+export const AllResources = Resources.items;
+// Helper for dedupe
+const unique = (array: Type[]) => Array.from(new Set(array));
+// Categories
+export const Categories: string[] = unique(
+ AllResources?.flatMap((item) => item.category),
+);
+// Regions
+export const Regions: string[] = unique(
+ AllResources?.flatMap((item) => item.region),
+);
+// Titles
+export const Titles: string[] = unique(AllResources?.map((item) => item.title));
diff --git a/app/(Pages)/resources/mockResourceRegistry.ts b/app/(Pages)/resources/mockResourceRegistry.ts
new file mode 100644
index 0000000..2a1aa98
--- /dev/null
+++ b/app/(Pages)/resources/mockResourceRegistry.ts
@@ -0,0 +1,12 @@
+import { createListCollection } from "@chakra-ui/react";
+import { Regions, Categories, Titles } from "./mockIndex";
+
+const toList = (arr: string[]) => arr.map((value) => ({ label: value, value }));
+
+export const resourceCollectionRegistry = {
+ Regions: createListCollection({ items: toList(Regions) }),
+ Categories: createListCollection({ items: toList(Categories) }),
+ Titles: createListCollection({ items: toList(Titles) }),
+};
+
+export type ResourceRegistryKey = keyof typeof resourceCollectionRegistry;
diff --git a/app/(Pages)/resources/page.tsx b/app/(Pages)/resources/page.tsx
new file mode 100644
index 0000000..296be52
--- /dev/null
+++ b/app/(Pages)/resources/page.tsx
@@ -0,0 +1,442 @@
+"use client";
+
+import {
+ Container,
+ Box,
+ CloseButton,
+ Input,
+ InputGroup,
+ Card,
+ HStack,
+ Heading,
+ Button,
+ Combobox,
+ Portal,
+ useFilter,
+ useListCollection,
+ Group,
+ ScrollArea,
+ Center,
+ Tag,
+ Stack,
+} from "@chakra-ui/react";
+import { useRef, useState } from "react";
+import checkDeviceSize from "@/components/ui/breakpoints";
+import { Icon } from "@/components/ui/icons/icon";
+import {
+ HeaderTemplate,
+ PageBuilder,
+} from "@/components/page-builder/template";
+
+import { resourceCollectionRegistry } from "./mockResourceRegistry";
+import { AllResources } from "./mockIndex";
+import { Tooltip } from "@/components/ui/tooltip";
+export default function SearchResources() {
+ //Retrieve mock data from json file
+ const [searchlist, setSearchlist] = useState(null);
+
+ const { contains } = useFilter({ sensitivity: "base" });
+
+ const [value, setValue] = useState("Initial value");
+ const inputRef = useRef(null);
+ const notMobileDevice = checkDeviceSize();
+
+ const endElement = value ? (
+ {
+ setValue("");
+ inputRef.current?.focus();
+ }}
+ me="-2"
+ />
+ ) : undefined;
+
+ const [formData, setFormData] = useState<{
+ searchRegion: string;
+ searchCategory: string;
+ searchQuery: string;
+ }>({
+ searchQuery: "",
+ searchRegion: "",
+ searchCategory: "",
+ });
+
+ const regionCollection = resourceCollectionRegistry.Regions;
+ const categoryCollection = resourceCollectionRegistry.Categories;
+ const titleCollection = resourceCollectionRegistry.Titles;
+
+ const { collection: regionDropDown, filter: filterRegions } =
+ useListCollection({
+ initialItems: regionCollection.items,
+ itemToString: (item) => item.label,
+ filter: contains,
+ });
+ const { collection: categoryDropDown, filter: filterCategories } =
+ useListCollection({
+ initialItems: categoryCollection.items,
+ itemToString: (item) => item.label,
+ filter: contains,
+ });
+ const { collection: titleDropDown, filter: filterTitles } = useListCollection(
+ {
+ initialItems: titleCollection.items,
+ itemToString: (item) => item.label,
+ filter: contains,
+ },
+ );
+ const [regionInput, setRegionInput] = useState("");
+ const [categoryInput, setCategoryInput] = useState("");
+ const [titleInput, setTitleInput] = useState("");
+ const [filteredItems, setFilteredItems] = useState(AllResources);
+
+ const applyFilters = () => {
+ const noFilters =
+ !formData.searchRegion &&
+ !formData.searchCategory &&
+ !formData.searchQuery;
+ if (noFilters) {
+ setFilteredItems(AllResources);
+ return;
+ }
+ let results = AllResources;
+ if (formData.searchRegion) {
+ results = results.filter((resources) =>
+ resources.region.includes(formData.searchRegion),
+ );
+ }
+ if (formData.searchCategory) {
+ results = results.filter((resources) =>
+ resources.category.includes(formData.searchCategory),
+ );
+ }
+ if (formData.searchQuery) {
+ const q = formData.searchQuery.toLowerCase();
+ results = results.filter(
+ (resources) =>
+ resources.title.toLowerCase().includes(q) ||
+ resources.description.toLowerCase().includes(q),
+ );
+ }
+ setFilteredItems(results);
+ };
+
+ return (
+ <>
+ {notMobileDevice ? (
+ <>
+
+
+
+
+
+
+
+ {
+ const value = e?.inputValue;
+ setRegionInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchRegion: value,
+ }));
+ if (!value) {
+ filterRegions("");
+ setFilteredItems(AllResources);
+ } else {
+ filterRegions(value);
+ applyFilters();
+ }
+ }}
+ >
+
+ {
+ const { value } = details?.currentTarget;
+ setRegionInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchRegion: value,
+ }));
+ if (!value) {
+ filterRegions("");
+ setFilteredItems(AllResources);
+ } else {
+ filterRegions(value);
+ applyFilters();
+ }
+ }}
+ onChange={(e) => {
+ const { value } = e?.target;
+ setRegionInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchRegion: value,
+ }));
+ if (!value) {
+ filterRegions("");
+ setFilteredItems(AllResources);
+ } else {
+ filterRegions(value);
+ applyFilters();
+ }
+ }}
+ />
+
+
+
+
+
+
+
+
+ No items found
+ {regionDropDown.items.map((item) => (
+
+ {item.label}
+
+
+ ))}
+
+
+
+
+ {
+ const value = e?.inputValue;
+ setCategoryInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchCategory: value,
+ }));
+ if (!value) {
+ filterCategories("");
+ setFilteredItems(AllResources);
+ } else {
+ filterCategories(value);
+ applyFilters();
+ }
+ }}
+ >
+
+ {
+ const { value } = details?.currentTarget;
+ setCategoryInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchCategory: value,
+ }));
+ if (!value) {
+ filterCategories("");
+ setFilteredItems(AllResources);
+ } else {
+ filterCategories(value);
+ applyFilters();
+ }
+ }}
+ onChange={(e) => {
+ const { value } = e?.target;
+ setCategoryInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchCategory: value,
+ }));
+ if (!value) {
+ filterCategories("");
+ setFilteredItems(AllResources);
+ }
+ filterCategories(value);
+ applyFilters();
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ No items found
+ {categoryDropDown.items.map((item) => (
+
+ {item.label}
+
+
+ ))}
+
+
+
+
+ {
+ const value = e?.inputValue;
+ setTitleInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchQuery: value,
+ }));
+ if (!value) {
+ filterTitles("");
+ setFilteredItems(AllResources);
+ }
+ filterTitles(value);
+ applyFilters();
+ }}
+ >
+
+ {
+ const { value } = e?.target;
+ setTitleInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchQuery: value,
+ }));
+ if (!value) {
+ filterTitles("");
+ setFilteredItems(AllResources);
+ }
+ filterTitles(value);
+ applyFilters();
+ }}
+ onSelect={(details) => {
+ const { value } = details?.currentTarget;
+ setTitleInput(value);
+ setFormData((prev) => ({
+ ...prev,
+ searchQuery: value,
+ }));
+ if (!value) {
+ filterTitles("");
+ setFilteredItems(AllResources);
+ }
+ filterTitles(value);
+ applyFilters();
+ }}
+ />
+
+
+
+
+
+
+
+
+ No items found
+ {titleDropDown.items.map((item) => (
+
+ {item.label}
+
+
+ ))}
+
+
+
+
+ {
+ if (!titleInput && !categoryInput && !regionInput) {
+ filterTitles("");
+ filterCategories("");
+ filterRegions("");
+ setFilteredItems(AllResources);
+ }
+ applyFilters();
+ }}
+ aria-label="Search Resources"
+ >
+
+
+
+ {/* CARD TABLE SECTION */}
+
+
+
+ {filteredItems.map((item) => (
+
+
+
+
+
+
+ {item.title}
+
+ {item.description}
+
+
+
+
+
+ {item.region.length > 0
+ ? `+${item.region.length} Regions`
+ : item.region}
+
+
+
+
+
+
+ {item.category.join(", ")}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ // This is a placeholder for the mobile view to be updated later
+ // Search
+
+
+
+
+ {/* */}
+
+ Search
+
+ {
+ setValue(e.currentTarget.value);
+ }}
+ />
+
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/app/(Pages)/search/mock.json b/app/(Pages)/search/mock.json
deleted file mode 100644
index e01c66a..0000000
--- a/app/(Pages)/search/mock.json
+++ /dev/null
@@ -1,32 +0,0 @@
-[
- {
- "title": "Educational Advancement",
- "link": "/search",
- "dataValue": "Deliver high-quality educational programs and conferences tailored to incarcerated and formerly incarcerated individuals, equipping them with essential skills for career success."
- },
- {
- "title": "Example 2",
- "link": "/search",
- "dataValue": "Value 2"
- },
- {
- "title": "Example 3",
- "link": "/search",
- "dataValue": "Value 3"
- },
- {
- "title": "Example 4",
- "link": "/search",
- "dataValue": "Value 4"
- },
- {
- "title": "Example 5",
- "link": "/search",
- "dataValue": "Value 5"
- },
- {
- "title": "Example 6",
- "link": "/search",
- "dataValue": "Value 6"
- }
-]
\ No newline at end of file
diff --git a/app/(Pages)/search/page.tsx b/app/(Pages)/search/page.tsx
deleted file mode 100644
index 33b1715..0000000
--- a/app/(Pages)/search/page.tsx
+++ /dev/null
@@ -1,205 +0,0 @@
-"use client"
-
-import { GemIcon } from '@/components/ui/icons';
-import {
- Container, Box, Text, CloseButton,
- Fieldset, Field, Input, InputGroup, AbsoluteCenter, Card, Stack, VStack, HStack, Heading,
- SimpleGrid
-} from '@chakra-ui/react';
-import { useRef, useState } from "react"
-import { poppins } from '@/components/ui/fonts';
-import checkDeviceSize from '@/components/ui/breakpoints';
-import mockData from '@/app/(Pages)/search/mock.json'
-
-
-import {
- Portal,
- useFilter,
- useListCollection,
-} from "@chakra-ui/react"
-
-type BlogPost = {
- title: string,
- id: number,
- dateFull: string,
- dateY: number,
- dateM: number,
- dateD: number
-}
-
-export default function Search() {
- //Retrieve mock data from json file
- const [searchlist, setSearchlist] = useState(null)
-
- const { contains } = useFilter({ sensitivity: "base" })
-
- const { collection, set, filter } = useListCollection({
- initialItems: [],
- itemToString: (item) => item.title,
- itemToValue: (item) => item.id.toString(),
- filter: contains,
- })
- // const handleInputChange = async (details: Fieldset.InputValueChangeDetails) => {
- // const query = details.inputValue
-
- // if (query.length < 2) return
- // const response = await fetch(`/api/blog/search?q=${query}`)
- // const data = await response.json()
-
- // set(data)
- // // Apply filter to the collection
- // filter(details.inputValue)
- // /////////////////
- // }
-
-
-
- //////////////////
- const [value, setValue] = useState("Initial value")
- const inputRef = useRef(null)
- const notMobileDevice = checkDeviceSize();
-
- const endElement = value ? (
- {
- setValue("")
- inputRef.current?.focus()
- }}
- me="-2"
- />
- ) : undefined
-
- return (
-
-
- <> {
- notMobileDevice ? (
-
-
-
-
-
-
-
-
- Search
-
-
-
-
-
-
- Search
- Enter your search query
-
-
-
- Search Data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/*
- collection={collection}
- onInputValueChange={handleInputChange}
- onValueChange={(details) => {
- const post = collection.items.find(item => item.id.toString() === details.value[0]);
- if (post) setSearchlist([post])
- }}
- >
- Search Data
-
-
-
-
-
-
-
-
-
-
-
- No items found
-
- {collection.items.map((item) => (
-
- {item.title}
-
- ))}
-
-
-
-
- */}
-
- {(searchlist ?? []).map((post) => (
-
- {post.title}
- {post.dateFull}
-
- ))}
-
-
-
-
-
-
-
-
- ) :
- // This is a placeholder for the mobile view to be updated later
- // Search
- (
-
-
-
-
-
- Search
-
- {
- setValue(e.currentTarget.value)
- }}
- />
-
-
-
-
-
- )
- }>
- );
-}
diff --git a/app/(Pages)/services/page.tsx b/app/(Pages)/services/page.tsx
index 0536208..3ae04d6 100644
--- a/app/(Pages)/services/page.tsx
+++ b/app/(Pages)/services/page.tsx
@@ -47,92 +47,65 @@ export default function Services() {
Whether you're seeking support for yourself or your organization, we
invite you to learn more about our services and how we can help."
>
- {services.map((service) => (
-
- ))}
+
+ {services.map((service) => (
+
+ ))}
+
-
-
-
-
-
- Services
-
- At MOKSE, we offer a range of services designed to empower and
- support our community
-
-
-
-
- Take the Next Step Today!
-
- Whether you're seeking support for yourself or your
- organization, we invite you to learn more about our services and how
- we can help.
-
-
- {services.map((service) => (
-
- ))}
-
-
-
-
-
-
-
Make a Difference – Get Involved!
-
- Are you passionate about helping justice-impacted individuals? We
- need compassionate volunteers to assist in various roles,
- including mentorship, tutoring, and administrative support.
-
+
+
+
+
Make a Difference – Get Involved!
+
+ Are you passionate about helping justice-impacted individuals?
+ We need compassionate volunteers to assist in various roles,
+ including mentorship, tutoring, and administrative support.
+
+
+
+ Get Involved
+
+
+
+
+
+
+ {supportOptions.map((option) => (
+
+ ))}
+
- Get Involved
+ Donate
-
-
-
-
-
- {supportOptions.map((option) => (
-
- ))}
-
-
-
- Donate
-
-
-
-
-
- Find Out How MoKse Supports Change
- Frequently Asked Questions
-
- {faqItems.map((item) => (
-
- ))}
-
-
-
-
-
+
+
+ Find Out How MoKse Supports Change
+ Frequently Asked Questions
+
+ {faqItems.map((item) => (
+
+ ))}
+
+
+
+
>
);
}
diff --git a/app/page.tsx b/app/page.tsx
index 72bdb7c..d413b71 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,343 +1,398 @@
-'use client'
+"use client";
-import NextLink from 'next/link';
+import NextLink from "next/link";
import FeatureCard from "../components/home/feature-card";
import { empowerment } from "../data/empowerment";
import { getInvolved } from "../data/get-involved";
-import { Container, Box, Text, Button, AbsoluteCenter, VStack, Heading, Link as ChakraLink, Center, SimpleGrid, GridItem, Stack, Card, Image as ChakraImage } from '@chakra-ui/react';
-import { RiArrowRightLine } from 'react-icons/ri';
-import { poppins } from '../components/ui/fonts';
-import checkDeviceSize from '../components/ui/breakpoints';
-
+import {
+ Container,
+ Box,
+ Text,
+ Button,
+ AbsoluteCenter,
+ VStack,
+ Heading,
+ Link as ChakraLink,
+ Center,
+ SimpleGrid,
+ GridItem,
+ Stack,
+ Card,
+ Image as ChakraImage,
+} from "@chakra-ui/react";
+import { RiArrowRightLine } from "react-icons/ri";
+import { poppins } from "../components/ui/fonts";
+import checkDeviceSize from "../components/ui/breakpoints";
+import { Icon } from "@/components/ui/icons/icon";
export default function Home() {
-
const notMobileDevice = checkDeviceSize();
return (
<>
- {
- notMobileDevice ? (
-
-
+
+
-
-
-
-
- Empowering change through education and advocacy
-
-
-
- We strive to break down barriers and stop the stigma associated with
- incarceration through consulting services, educational conferences, and business support programs.
-
-
-
-
- Learn More
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A Commitment To Empowerment
+
+
+
+
+ Empowering change through education and advocacy
-
- To transform the lives of learners by providing accessible education, entrepreneurial
- support, and empowerment resources that foster personal and professional growth.
-
-
-
-
- About Us
-
-
-
-
-
- {empowerment.map((f) => (
-
- ))}
-
-
-
-
-
-
-
-
-
+ We strive to break down barriers and stop the stigma
+ associated with incarceration through consulting services,
+ educational conferences, and business support programs.
+
+
+
+
+ Learn More
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+ To transform the lives of learners by providing accessible
+ education, entrepreneurial support, and empowerment resources
+ that foster personal and professional growth.
+
+
+
+
+ About Us
+
+
+
+
+
+
+ {empowerment.map((f) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- Make a Difference - Get Involved!
- Are you passionate about helping justice-impacted individuals?
- We need compassionate volunteers to assist in various roles, including
- mentorship, tutoring, and administrative support.
- Get Involved
-
-
-
-
-
-
-
-
- {getInvolved.map((f) => (
+ >
+
+
+
+
+
+
+ Make a Difference - Get Involved!
+
+ Are you passionate about helping justice-impacted
+ individuals? We need compassionate volunteers to assist in
+ various roles, including mentorship, tutoring, and
+ administrative support.
+
+ Get Involved
+
+
+
+
+
+
+ {getInvolved.map((f) => (
))}
-
-
-
-
-
- Donate
-
-
-
-
+
+
+
+
+ Donate
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Special Story: The three words that changed my life
+
+
+
+ Mokse is proud to share the TEDx talk of Dr. Matthews.
+
+
+
-
-
-
-
-
-
-
-
-
- Special Story: The three words that changed my life
-
-
- Mokse is proud to share the TEDx talk of Dr. Matthews.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
- ) : (
-
-
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
-
-
-
- Empowering Change Through Education And Advocacy
-
- We strive to break down barriers and stop the stigma associated with
- incarceration through consulting services, educational conferences, and business support programs.
-
-
-
- Learn More
-
-
-
-
-
-
-
-
-
- A Commitment to Empowerment
+
+
+
+ Empowering Change Through Education And Advocacy
+
- To transform the lives of learners by providing accessible education, entrepreneurial
- support, and empowerment resources that foster personal and professional growth.
-
-
-
- About Us
-
-
-
-
-
- {empowerment.map((f) => (
-
- ))}
-
-
- {/*
-
- Make a Difference – Get Involved!
- Are you passionate about helping justice-impacted inBoxiduals?
- We need compassionate volunteers to assist in various roles, including
- mentorship, tutoring, and administrative support.
- Get Involved
-
-
-
- {getInvolved.map((f) => (
-
- ))}
-
- Donate
-
-
- special story: The Three Words That Change My Life.
- Mokse is proud to share the TEDx talk of Dr. Matthews.
-
- VIDEO
-
-
+
+ We strive to break down barriers and stop the stigma
+ associated with incarceration through consulting services,
+ educational conferences, and business support programs.
+
+
+
+
+ Learn More
+
+
+
+
+
+
+
+
-
-
+
+ A Commitment to Empowerment
+
+
+ To transform the lives of learners by providing accessible
+ education, entrepreneurial support, and empowerment resources
+ that foster personal and professional growth.
+
+
+
+
+ About Us
+
+
+
-
-
-
-
-
- */}
-
- )
- }
-
+
+ {empowerment.map((f) => (
+
+ ))}
+
+
+
+ )}
>
);
}
diff --git a/components/common/footer.tsx b/components/common/footer.tsx
index f2289d0..aeac678 100644
--- a/components/common/footer.tsx
+++ b/components/common/footer.tsx
@@ -13,25 +13,16 @@ import {
Heading,
Center,
IconButton,
- Icon,
Image,
Box,
Flex,
} from "@chakra-ui/react";
-import { useColorMode } from "../ui/color-mode";
+import { useColorMode, useColorModeValue } from "../ui/color-mode";
import { poppins } from "../ui/fonts";
import checkDeviceSize from "../ui/breakpoints";
-import {
- FB,
- LinkedIn,
- YouTube,
- Instagram,
- PhoneIcon,
- MapPinIcon,
- MailIcon,
- AngleRightIcon,
-} from "../ui/icons";
+
+import { Icon } from "@/components/ui/icons/icon";
export default function Footer() {
const { colorMode } = useColorMode();
@@ -54,11 +45,10 @@ export default function Footer() {
-
+
@@ -104,7 +94,7 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
>
-
+
@@ -124,7 +114,7 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
>
-
+
@@ -144,7 +134,7 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
>
-
+
@@ -164,7 +154,7 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
>
-
+
@@ -187,10 +177,10 @@ export default function Footer() {
-
Home
@@ -200,10 +190,10 @@ export default function Footer() {
-
About Us
@@ -213,10 +203,10 @@ export default function Footer() {
-
Services
@@ -226,10 +216,10 @@ export default function Footer() {
-
Contact
@@ -303,7 +293,12 @@ export default function Footer() {
{/*
test
*/}
-
+
497 Hooksett Road,
Suite 362,
@@ -313,7 +308,12 @@ export default function Footer() {
-
+
{/*
test
*/}
@@ -326,7 +326,7 @@ export default function Footer() {
-
+
{/*
*/}
diff --git a/components/common/iconbutton.tsx b/components/common/iconbutton.tsx
new file mode 100644
index 0000000..0b5cb6a
--- /dev/null
+++ b/components/common/iconbutton.tsx
@@ -0,0 +1,25 @@
+// Create a reusable IconButton component that takes in link, platform, url, and icon as props and renders an anchor tag with the provided icon.
+// & React.ComponentProps
+
+export default function IconButton({
+ link,
+ platform,
+ url,
+ icon,
+}: {
+ link: string;
+ platform: string;
+ url: string;
+ icon: React.ReactNode;
+}) {
+ return (
+
+ {icon}
+
+ );
+}
diff --git a/components/common/navbar.tsx b/components/common/navbar.tsx
index 78ee2f8..efca854 100644
--- a/components/common/navbar.tsx
+++ b/components/common/navbar.tsx
@@ -1,141 +1,161 @@
-'use client';
+"use client";
-import Image from 'next/image';
-import NextLink from 'next/link';
-import { HStack, Container, Link as ChakraLink, Button, Text } from '@chakra-ui/react';
-import { MdBrightness4 as MdMoon, MdBrightness5 as MdSun } from 'react-icons/md';
-import { useColorMode } from '../ui/color-mode';
-import { useEffect, useState } from 'react';
-import { openSans } from '../ui/fonts';
-import useDeviceSize from '../ui/breakpoints';
+import Image from "next/image";
+import NextLink from "next/link";
+import {
+ HStack,
+ Container,
+ Link as ChakraLink,
+ Button,
+ Text,
+} from "@chakra-ui/react";
+import {
+ MdBrightness4 as MdMoon,
+ MdBrightness5 as MdSun,
+} from "react-icons/md";
+import { useColorMode } from "../ui/color-mode";
+import { useEffect, useState } from "react";
+import { openSans } from "../ui/fonts";
+import useDeviceSize from "../ui/breakpoints";
export default function Navbar() {
- const { colorMode, toggleColorMode } = useColorMode();
- const deviceSize = useDeviceSize();
- const notMobileDevice =
- deviceSize !== 'base'
- &&
- deviceSize !== 'sm';
+ const { colorMode, toggleColorMode } = useColorMode();
+ const deviceSize = useDeviceSize();
+ const notMobileDevice = deviceSize !== "base" && deviceSize !== "sm";
- const [isMounted, setIsMounted] = useState(false);
- const [isFixed, setIsFixed] = useState(false);
+ const [isMounted, setIsMounted] = useState(false);
+ const [isFixed, setIsFixed] = useState(false);
- // Mount check for screen size check
- useEffect(() => {
- setIsMounted(true);
- }, []);
+ // Mount check for screen size check
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
- // Check device scroll position for navbar position
- useEffect(() => {
- if (!isMounted) return;
- const handleScroll = () => {
- const scrollPosition = window?.scrollY;
- setIsFixed(scrollPosition >= 50);
- };
- handleScroll();
- window?.addEventListener('scroll', handleScroll);
- return () => window?.removeEventListener('scroll', handleScroll);
- }, [isMounted]);
+ // Check device scroll position for navbar position
+ useEffect(() => {
+ if (!isMounted) return;
+ const handleScroll = () => {
+ const scrollPosition = window?.scrollY;
+ setIsFixed(scrollPosition >= 50);
+ };
+ handleScroll();
+ window?.addEventListener("scroll", handleScroll);
+ return () => window?.removeEventListener("scroll", handleScroll);
+ }, [isMounted]);
- // Container props
- const ContainerProps = {
- h: notMobileDevice ? ('10vh') : ('5vh'),
- zIndex: 1,
- position: isFixed ? ('fixed') : ('absolute'),
- fluid: true,
- py:
- isFixed ?
- notMobileDevice ? (8) : (2) :
- notMobileDevice ? (8) : (5),
- transition: "all 0.2s ease-in-out",
- transform:
- isFixed ? ('translateY(0)') : ('translateY(-20px)'),
- opacity: isFixed ? 1 : 0.9,
- boxShadow: isFixed ? '2xl' : 'none',
- bg:
- isFixed ?
- colorMode === "light" ? 'black'
- : 'blackAlpha.950'
- : 'transparent',
- className: openSans.className,
- } as const;
+ // Container props
+ const ContainerProps = {
+ h: notMobileDevice ? "10vh" : "5vh",
+ zIndex: 1,
+ position: isFixed ? "fixed" : "absolute",
+ fluid: true,
+ py: isFixed ? (notMobileDevice ? 8 : 2) : notMobileDevice ? 8 : 5,
+ transition: "all 0.2s ease-in-out",
+ transform: isFixed ? "translateY(0)" : "translateY(-20px)",
+ opacity: isFixed ? 1 : 0.9,
+ boxShadow: isFixed ? "2xl" : "none",
+ bg: isFixed
+ ? colorMode === "light"
+ ? "black"
+ : "blackAlpha.950"
+ : "transparent",
+ className: openSans.className,
+ } as const;
- // Nav text props
- const navTextProps = {
- fontSize: '24px',
- fontWeight: 600,
- _light: { color: 'white' },
- } as const;
+ // Nav text props
+ const navTextProps = {
+ fontSize: "16px",
+ fontWeight: 600,
+ _light: { color: "white" },
+ } as const;
- return (
-
-
-
- {notMobileDevice ? (
-
-
-
-
-
-
-
-
-
- Home
-
-
-
-
- About Us
-
-
-
-
- Services
-
-
-
-
- Stop The Stigma
-
-
-
-
- Contact
-
-
-
-
-
- Call Us Today
-
-
-
- {isMounted && (colorMode === "light" ? : )}
-
-
- ) : (
-
-
-
-
-
-
-
- Menu
-
-
- )}
-
-
- );
+ return (
+
+
+ {notMobileDevice ? (
+
+
+
+
+
+
+
+
+
+ About
+
+
+
+
+ Contact
+
+
+
+
+ Services
+
+
+
+
+ Stop The Stigma
+
+
+
+
+ Resources
+
+
+
+
+
+ Call Us Today
+
+
+
+
+ {" "}
+ {isMounted && (colorMode === "light" ? : )}
+
+
+
+ ) : (
+
+
+
+
+
+
+ Menu
+
+ )}
+
+
+ );
}
diff --git a/components/contact/contact-form.tsx b/components/contact/contact-form.tsx
index b514688..6a87fb7 100644
--- a/components/contact/contact-form.tsx
+++ b/components/contact/contact-form.tsx
@@ -1,6 +1,18 @@
-'use client';
+"use client";
-import { useState, FormEvent } from 'react';
+import {
+ Box,
+ Button,
+ Card,
+ Container,
+ Field,
+ Fieldset,
+ Input,
+ Stack,
+ Textarea,
+} from "@chakra-ui/react";
+import { useState, FormEvent } from "react";
+import { toaster, Toaster } from "../ui/toaster";
interface FormData {
name: string;
@@ -10,30 +22,63 @@ interface FormData {
}
interface SubmissionStatus {
- type: 'success' | 'error' | null;
+ type: "success" | "error" | null;
message: string;
}
export default function ContactForm() {
const [formData, setFormData] = useState({
- name: '',
- email: '',
- subject: '',
- message: '',
+ name: "",
+ email: "",
+ subject: "",
+ message: "",
});
const [isSubmitting, setIsSubmitting] = useState(false);
- const [status, setStatus] = useState({ type: null, message: '' });
+ const submitToasterId = "contact-form-submission";
+ const [showToast, setShowToast] = useState(false);
+ const [toastMessage, setToastMessage] = useState({
+ error: () => "",
+ loading: () =>
+ toaster.loading({
+ id: submitToasterId,
+ title: "Submitting form",
+ description: "Please wait while we submit your message.",
+ type: "info",
+ closable: true,
+ }),
+ update: () => "",
+ updateSuccess: () =>
+ toaster.update(submitToasterId, {
+ title: "Form submitted",
+ description: "Your message has been sent successfully.",
+ type: "success",
+ duration: 3000,
+ closable: true,
+ }),
+ reset: () =>
+ toaster.create({
+ title: "Form reset",
+ description: "The contact form has been cleared.",
+ type: "info",
+ closable: true,
+ }),
+ });
+ const [status, setStatus] = useState({
+ type: null,
+ message: "",
+ });
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
- setStatus({ type: null, message: '' });
+ setStatus({ type: null, message: "" });
+ if (toaster.isVisible(submitToasterId)) return toastMessage.loading();
try {
- const response = await fetch('@/app/(API Routes)/api/contact/route', {
- method: 'POST',
+ const response = await fetch("/api/contact", {
+ method: "POST",
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
@@ -42,101 +87,131 @@ export default function ContactForm() {
if (response.ok) {
setStatus({
- type: 'success',
- message: 'Thank you for your message! We will get back to you soon.',
+ type: "success",
+ message: "Thank you for your message! We will get back to you soon.",
});
setFormData({
- name: '',
- email: '',
- subject: '',
- message: '',
+ name: "",
+ email: "",
+ subject: "",
+ message: "",
});
} else {
setStatus({
- type: 'error',
- message: data.error || 'Something went wrong. Please try again.',
+ type: "error",
+ message: data.error || "Something went wrong. Please try again.",
});
}
} catch {
setStatus({
- type: 'error',
- message: 'Failed to send message. Please try again later.',
+ type: "error",
+ message: "Failed to send message. Please try again later.",
});
} finally {
setIsSubmitting(false);
+ setTimeout(() => {
+ toastMessage.updateSuccess();
+ }, 3000);
}
};
return (
-
+
+
+
);
}
diff --git a/components/home/feature-card.tsx b/components/home/feature-card.tsx
index ddbc190..c9ac87d 100644
--- a/components/home/feature-card.tsx
+++ b/components/home/feature-card.tsx
@@ -1,33 +1,30 @@
-'use client'
+"use client";
-import type { ReactNode } from 'react';
-import { Card, Avatar, Icon, Text } from '@chakra-ui/react';
-
-import { poppins } from '../ui/fonts';
+import { Card, Avatar, Text } from "@chakra-ui/react";
+import { Icon } from "../ui/icons/icon";
+import { poppins } from "../ui/fonts";
+import { IconName } from "../ui/icons/icon-registry";
type FeatureCardProps = {
title: string;
description: string;
- icon: ReactNode;
+ icon: IconName;
};
-
export default function FeatureCard({
title,
description,
icon,
}: FeatureCardProps) {
-
return (
-
-
- {icon}
-
+
+
+
+
+
{title}
-
- {description}
-
+ {description}
-
+
);
-}
\ No newline at end of file
+}
diff --git a/components/services/faq-item.tsx b/components/services/faq-item.tsx
index 444ce4b..dc623ac 100644
--- a/components/services/faq-item.tsx
+++ b/components/services/faq-item.tsx
@@ -1,8 +1,8 @@
-'use client';
+"use client";
-import { useState } from 'react';
-import { PlusIcon, MinusIcon } from '../ui/icons';
-import { Button } from '@chakra-ui/react';
+import { useState } from "react";
+import { Button } from "@chakra-ui/react";
+import { Icon } from "../ui/icons/icon";
interface FaqItemProps {
question: string;
@@ -16,7 +16,7 @@ export default function FaqItem({ question, answer }: FaqItemProps) {
setIsOpen(!isOpen)} aria-expanded={isOpen}>
{question}
- {isOpen ? : }
+ {isOpen ? : }
{isOpen && {answer}
}
diff --git a/components/services/service-card.tsx b/components/services/service-card.tsx
index 3096155..68a621e 100644
--- a/components/services/service-card.tsx
+++ b/components/services/service-card.tsx
@@ -1,7 +1,11 @@
-import Image from 'next/image';
+"use client";
+import Image from "next/image";
+import { IconName } from "../ui/icons/icon-registry";
+import { Icon } from "../ui/icons/icon";
+import { Card, Image as ChakraImage, Heading } from "@chakra-ui/react";
interface ServiceCardProps {
- icon: React.ComponentType;
+ icon: IconName;
title: string;
description: string;
image: string;
@@ -9,18 +13,31 @@ interface ServiceCardProps {
}
export default function ServiceCard({
- icon: Icon,
+ icon,
title,
description,
image,
imageAlt,
}: ServiceCardProps) {
return (
-
-
- {title}
- {description}
-
-
+
+
+
+
+
+
+ {title}
+ {description}
+
+
);
}
diff --git a/components/services/support-card.tsx b/components/services/support-card.tsx
index d021769..36e5b87 100644
--- a/components/services/support-card.tsx
+++ b/components/services/support-card.tsx
@@ -1,17 +1,20 @@
+import { IconName } from "../ui/icons/icon";
+import { Icon } from "../ui/icons/icon";
+
interface SupportCardProps {
- icon: React.ComponentType;
+ icon: IconName;
title: string;
description: string;
}
export default function SupportCard({
- icon: Icon,
+ icon,
title,
description,
}: SupportCardProps) {
return (
-
+
{title}
{description}
diff --git a/components/ui/icons/icon-registry.ts b/components/ui/icons/icon-registry.ts
new file mode 100644
index 0000000..4d653c5
--- /dev/null
+++ b/components/ui/icons/icon-registry.ts
@@ -0,0 +1,31 @@
+// icon-registry.ts
+import * as Icons from "./index";
+
+export const iconRegistry = {
+ Cube: Icons.FaCube,
+ Plug: Icons.FaPlug,
+ UsersCog: Icons.FaUsersCog,
+ Newspaper: Icons.FaRegNewspaper,
+ Users: Icons.FaUsers,
+ Gem: Icons.FaRegGem,
+ ArrowRight: Icons.FaLongArrowAltRight,
+ Minus: Icons.FaMinus,
+ Plus: Icons.FaPlus,
+ MapPin: Icons.FaMapPin,
+ Facebook: Icons.FaFacebook,
+ FB: Icons.FaFacebook,
+ LinkedIn: Icons.FaLinkedin,
+ YouTube: Icons.FaYoutube,
+ Instagram: Icons.FaInstagram,
+ Phone: Icons.FaPhoneAlt,
+ Mail: Icons.FaEnvelope,
+ AngleRight: Icons.FaAngleRight,
+ BookOpen: Icons.FaBookOpen,
+ Megaphone: Icons.FaBullhorn,
+ Network: Icons.FaNetworkWired,
+ HandHoldingHeart: Icons.FaHandHoldingHeart,
+ Handshake: Icons.FaHandshake,
+ Donate: Icons.FaDonate,
+ Search: Icons.FaSearch,
+};
+export type IconName = keyof typeof iconRegistry;
diff --git a/components/ui/icons/icon.tsx b/components/ui/icons/icon.tsx
new file mode 100644
index 0000000..fdf8616
--- /dev/null
+++ b/components/ui/icons/icon.tsx
@@ -0,0 +1,24 @@
+"use client";
+import { iconRegistry, IconName } from "./icon-registry";
+import { Icon as ChakraIcon } from "@chakra-ui/react";
+
+// export type IconName = keyof typeof iconRegistry;
+
+type IconProps = {
+ name: IconName;
+ size?: number;
+ color?: string;
+} & React.ComponentProps;
+
+export function Icon({ name, ...props }: IconProps) {
+ const Component = iconRegistry[name];
+ if (!Component) return null;
+ return (
+
+ );
+}
diff --git a/components/ui/icons/index.ts b/components/ui/icons/index.ts
index 53548dc..2eb4c94 100644
--- a/components/ui/icons/index.ts
+++ b/components/ui/icons/index.ts
@@ -1,4 +1,4 @@
-import {
+export {
FaCube,
FaPlug,
FaUsersCog,
@@ -16,24 +16,11 @@ import {
FaPhoneAlt,
FaEnvelope,
FaAngleRight,
+ FaBookOpen,
+ FaBullhorn,
+ FaNetworkWired,
+ FaHandHoldingHeart,
+ FaHandshake,
+ FaDonate,
+ FaSearch,
} from "react-icons/fa";
-
-export {
- FaCube as CubeIcon,
- FaPlug as PlugIcon,
- FaUsersCog as UsersCogIcon,
- FaRegNewspaper as NewspaperIcon,
- FaUsers as UsersIcon,
- FaRegGem as GemIcon,
- FaLongArrowAltRight as ArrowRightIcon,
- FaMinus as MinusIcon,
- FaPlus as PlusIcon,
- FaMapPin as MapPinIcon,
- FaFacebook as FB,
- FaLinkedin as LinkedIn,
- FaYoutube as YouTube,
- FaInstagram as Instagram,
- FaPhoneAlt as PhoneIcon,
- FaEnvelope as MailIcon,
- FaAngleRight as AngleRightIcon,
-};
diff --git a/data/contact.tsx b/data/contact.tsx
index 5735655..5293680 100644
--- a/data/contact.tsx
+++ b/data/contact.tsx
@@ -1,4 +1,5 @@
-import type { ReactNode } from 'react';
+import type { ReactNode } from "react";
+import { IconName } from "@/components/ui/icons/icon-registry";
export interface ContactInfo {
icon: ReactNode;
@@ -8,9 +9,8 @@ export interface ContactInfo {
}
export interface SocialLink {
- platform: string;
+ platform: IconName;
url: string;
- icon: ReactNode;
}
// FontAwesome 5.x SVG Icons - extracted from original mokse.org
@@ -136,44 +136,41 @@ const InstagramIcon = () => (
export const contactInfo: ContactInfo[] = [
{
icon: ,
- label: 'Mailing Address',
- value: '497 Hooksett Road Suite 362 Manchester NH 03104',
+ label: "Mailing Address",
+ value: "497 Hooksett Road Suite 362 Manchester NH 03104",
},
{
icon: ,
- label: 'Call Us On',
- value: '+1 603 496 1535',
- href: 'tel:+16034961535',
+ label: "Call Us On",
+ value: "+1 603 496 1535",
+ href: "tel:+16034961535",
},
{
icon: ,
- label: 'Email us',
- value: 'info@mokse.org',
- href: 'mailto:info@mokse.org',
+ label: "Email us",
+ value: "info@mokse.org",
+ href: "mailto:info@mokse.org",
},
];
export const socialLinks: SocialLink[] = [
{
- platform: 'Facebook',
- url: 'https://web.facebook.com/mokseorg/',
- icon: ,
+ platform: "Facebook",
+ url: "https://web.facebook.com/mokseorg/",
},
{
- platform: 'Youtube',
- url: 'https://www.youtube.com/@mokse-org',
- icon: ,
+ platform: "YouTube",
+ url: "https://www.youtube.com/@mokse-org",
},
{
- platform: 'Linkedin',
- url: 'https://www.linkedin.com/company/mokse-org',
- icon: ,
+ platform: "LinkedIn",
+ url: "https://www.linkedin.com/company/mokse-org",
},
{
- platform: 'Instagram',
- url: 'https://www.instagram.com/mokse_org/',
- icon: ,
+ platform: "Instagram",
+ url: "https://www.instagram.com/mokse_org/",
},
];
-export const mapEmbedUrl = 'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2921.334934398748!2d-71.41689792346378!3d42.99457907114612!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89e24f6482e3dde3%3A0x8b5f5e5e5e5e5e5e!2sMokse!5e0!3m2!1sen!2sus!4v1234567890123!5m2!1sen!2sus';
+export const mapEmbedUrl =
+ "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2921.334934398748!2d-71.41689792346378!3d42.99457907114612!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89e24f6482e3dde3%3A0x8b5f5e5e5e5e5e5e!2sMokse!5e0!3m2!1sen!2sus!4v1234567890123!5m2!1sen!2sus";
diff --git a/data/empowerment.tsx b/data/empowerment.tsx
index 5af5fa1..e7fdbbe 100644
--- a/data/empowerment.tsx
+++ b/data/empowerment.tsx
@@ -1,35 +1,34 @@
-import { Users, BookOpen, Megaphone, Network } from 'lucide-react';
-import type { ReactNode } from 'react';
+import { IconName } from "@/components/ui/icons/icon-registry";
export type Feature = {
- icon: ReactNode;
+ icon: IconName;
title: string;
description: string;
};
export const empowerment: Feature[] = [
{
- icon: ,
- title: 'Educational Advancement',
+ icon: "BookOpen",
+ title: "Educational Advancement",
description:
- 'Deliver high-quality educational programs and conferences tailored to incarcerated and formerly incarcerated individuals, equipping them with essential skills for career success.',
+ "Deliver high-quality educational programs and conferences tailored to incarcerated and formerly incarcerated individuals, equipping them with essential skills for career success.",
},
{
- icon: ,
- title: 'Entrepreneurial Empowerment',
+ icon: "Users",
+ title: "Entrepreneurial Empowerment",
description:
- 'Develop and support entrepreneurship initiatives, providing business resources, mentorship, and guidance to foster self-employment and economic independence.',
+ "Develop and support entrepreneurship initiatives, providing business resources, mentorship, and guidance to foster self-employment and economic independence.",
},
{
- icon: ,
- title: 'Stigma Reduction & Advocacy',
+ icon: "Megaphone",
+ title: "Stigma Reduction & Advocacy",
description:
- 'Advocate for policies and cultural change through consulting services and partnerships, aiming to dismantle barriers and eliminate the stigma associated with incarceration.',
+ "Advocate for policies and cultural change through consulting services and partnerships, aiming to dismantle barriers and eliminate the stigma associated with incarceration.",
},
{
- icon: ,
- title: 'Community Building',
+ icon: "Network",
+ title: "Community Building",
description:
- 'Establish a supportive network for formerly incarcerated individuals, encouraging reintegration and connection through meaningful professional and social networks.',
+ "Establish a supportive network for formerly incarcerated individuals, encouraging reintegration and connection through meaningful professional and social networks.",
},
-];
\ No newline at end of file
+];
diff --git a/data/get-involved.tsx b/data/get-involved.tsx
index aa2e842..abaa1a0 100644
--- a/data/get-involved.tsx
+++ b/data/get-involved.tsx
@@ -1,23 +1,22 @@
-import { Handshake, HandHeart} from 'lucide-react';
-import type { ReactNode } from 'react';
+import { IconName } from "@/components/ui/icons/icon-registry";
export type Feature = {
- icon: ReactNode;
+ icon: IconName;
title: string;
description: string;
};
export const getInvolved: Feature[] = [
{
- icon: ,
- title: 'Donate',
+ icon: "HandHoldingHeart",
+ title: "Donate",
description:
- 'Your donation goes directly toward supporting our programs and services, creating a positive impact on the lives of justice-impacted individuals. Every contribution, big or small, makes a difference.',
+ "Your donation goes directly toward supporting our programs and services, creating a positive impact on the lives of justice-impacted individuals. Every contribution, big or small, makes a difference.",
},
{
- icon: ,
- title: 'Partnership Opportunities',
+ icon: "Handshake",
+ title: "Partnership Opportunities",
description:
- 'We welcome partnerships with businesses, educational institutions, state agencies, and other nonprofits. Join us in creating meaningful change through collaborative projects and shared resources.',
- }
-];
\ No newline at end of file
+ "We welcome partnerships with businesses, educational institutions, state agencies, and other nonprofits. Join us in creating meaningful change through collaborative projects and shared resources.",
+ },
+];
diff --git a/data/services.ts b/data/services.ts
index 17649e1..1da1327 100644
--- a/data/services.ts
+++ b/data/services.ts
@@ -1,7 +1,6 @@
-import { CubeIcon, PlugIcon, UsersCogIcon, NewspaperIcon, UsersIcon } from '@/components/ui/icons/index';
-
+import { IconName } from "@/components/ui/icons/icon-registry";
export interface Service {
- icon: React.ComponentType;
+ icon: IconName;
title: string;
description: string;
image: string;
@@ -9,7 +8,7 @@ export interface Service {
}
export interface SupportOption {
- icon: React.ComponentType;
+ icon: IconName;
title: string;
description: string;
}
@@ -21,56 +20,65 @@ export interface FaqItem {
export const services: Service[] = [
{
- icon: CubeIcon,
- title: 'Technical Assistance and Consulting for Colleges and Universities',
- description: 'To provide consulting services to colleges and universities interested in establishing prison programs for incarcerated learners using micro-credentials and badging.',
- image: '/assets/services/technical-assistance.jpg',
- imageAlt: 'Technical Assistance',
+ icon: "Cube",
+ title: "Technical Assistance and Consulting for Colleges and Universities",
+ description:
+ "To provide consulting services to colleges and universities interested in establishing prison programs for incarcerated learners using micro-credentials and badging.",
+ image: "/assets/services/technical-assistance.jpg",
+ imageAlt: "Technical Assistance",
},
{
- icon: PlugIcon,
- title: 'Stop the Stigma Conference',
- description: 'To organize an annual conference that raises awareness and educates college students and community members about the issues surrounding incarceration and the importance of stopping the stigma.',
- image: '/assets/services/stop-stigma.jpg',
- imageAlt: 'Stop the Stigma Conference',
+ icon: "Plug",
+ title: "Stop the Stigma Conference",
+ description:
+ "To organize an annual conference that raises awareness and educates college students and community members about the issues surrounding incarceration and the importance of stopping the stigma.",
+ image: "/assets/services/stop-stigma.jpg",
+ imageAlt: "Stop the Stigma Conference",
},
{
- icon: UsersCogIcon,
- title: 'Entrepreneurship Support for Formerly Incarcerated Individuals',
- description: 'To provide support and resources to formerly incarcerated individuals who aspire to start their businesses',
- image: '/assets/services/entrepreneurship.jpg',
- imageAlt: 'Entrepreneurship Support',
+ icon: "UsersCog",
+ title: "Entrepreneurship Support for Formerly Incarcerated Individuals",
+ description:
+ "To provide support and resources to formerly incarcerated individuals who aspire to start their businesses",
+ image: "/assets/services/entrepreneurship.jpg",
+ imageAlt: "Entrepreneurship Support",
},
];
export const supportOptions: SupportOption[] = [
{
- icon: NewspaperIcon,
- title: 'Donate',
- description: 'Your donation goes directly toward supporting our programs and services, creating a positive impact on the lives of justice-impacted individuals. Every contribution, big or small, makes a difference.',
+ icon: "Donate",
+ title: "Donate",
+ description:
+ "Your donation goes directly toward supporting our programs and services, creating a positive impact on the lives of justice-impacted individuals. Every contribution, big or small, makes a difference.",
},
{
- icon: UsersIcon,
- title: 'Partnership Opportunities',
- description: 'We welcome partnerships with businesses, educational institutions, state agencies, and other nonprofits. Join us in creating meaningful change through collaborative projects and shared resources.',
+ icon: "Users",
+ title: "Partnership Opportunities",
+ description:
+ "We welcome partnerships with businesses, educational institutions, state agencies, and other nonprofits. Join us in creating meaningful change through collaborative projects and shared resources.",
},
];
export const faqItems: FaqItem[] = [
{
- question: 'What is the vision of Mokse?',
- answer: 'Mokse envisions a society where all individuals have equal opportunities to thrive, learn, and lead, free from the stigma and limitations associated with their pasts. We believe in empowering individuals to contribute positively to their communities.',
+ question: "What is the vision of Mokse?",
+ answer:
+ "Mokse envisions a society where all individuals have equal opportunities to thrive, learn, and lead, free from the stigma and limitations associated with their pasts. We believe in empowering individuals to contribute positively to their communities.",
},
{
- question: 'How can I get involved with Mokse?',
- answer: 'There are several ways to get involved, including volunteering, attending our events, or supporting our programs. For additional information on volunteering email: volunteers@mokse.org.',
+ question: "How can I get involved with Mokse?",
+ answer:
+ "There are several ways to get involved, including volunteering, attending our events, or supporting our programs. For additional information on volunteering email: volunteers@mokse.org.",
},
{
- question: 'How does Mokse support formerly incarcerated individuals?',
- answer: 'Mokse provides accessible education, entrepreneurial support, and empowerment resources designed to transform the lives of formerly incarcerated individuals, helping them achieve personal and professional growth.',
+ question: "How does Mokse support formerly incarcerated individuals?",
+ answer:
+ "Mokse provides accessible education, entrepreneurial support, and empowerment resources designed to transform the lives of formerly incarcerated individuals, helping them achieve personal and professional growth.",
},
{
- question: 'Where can I find more information about upcoming events?',
- answer: 'You can find details about our upcoming events, including the Stop the Stigma Conference, on our website. We regularly update it with new information on conferences, workshops, and community initiatives.',
+ question: "Where can I find more information about upcoming events?",
+ answer:
+ "You can find details about our upcoming events, including the Stop the Stigma Conference, on our website. We regularly update it with new information on conferences, workshops, and community initiatives.",
},
];
diff --git a/next.config.ts b/next.config.ts
index 8288bc4..a66ee15 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -4,6 +4,9 @@ const nextConfig: NextConfig = {
/* config options here */
// output: 'standalone',
+ experimental: {
+ optimizePackageImports: ["@chakra-ui/react"],
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 28cbb7a..7e36a42 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,7 @@
"name": "k",
"version": "0.1.0",
"dependencies": {
- "@chakra-ui/react": "^3.30.0",
+ "@chakra-ui/react": "^3.31.0",
"@emotion/react": "^11.14.0",
"lucide-react": "^0.562.0",
"next": "16.1.1",
@@ -344,9 +344,10 @@
}
},
"node_modules/@chakra-ui/react": {
- "version": "3.30.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.30.0.tgz",
- "integrity": "sha512-eIRRAilqY4f2zN8GWRnjcciBYsvy3GZDOmzGD9xk596LBxCTNCJaivdBiHCcgNlqA3y1wMyM1jepy2b2vQC4QA==",
+ "version": "3.31.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.31.0.tgz",
+ "integrity": "sha512-puvrZOfnfMA+DckDcz0UxO20l7TVhwsdQ9ksCv4nIUB430yuWzon0yo9fM10lEr3hd7BhjZARpMCVw5u280clw==",
+ "license": "MIT",
"dependencies": {
"@ark-ui/react": "^5.29.1",
"@emotion/is-prop-valid": "^1.4.0",
diff --git a/package.json b/package.json
index 6c6c248..a2e74ee 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"lint": "eslint"
},
"dependencies": {
- "@chakra-ui/react": "^3.30.0",
+ "@chakra-ui/react": "^3.31.0",
"@emotion/react": "^11.14.0",
"lucide-react": "^0.562.0",
"next": "16.1.1",
diff --git a/public/assets/services/1112.jpg b/public/assets/services/1112.jpg
new file mode 100644
index 0000000..0f791c3
Binary files /dev/null and b/public/assets/services/1112.jpg differ
diff --git a/tsconfig.json b/tsconfig.json
index f9b8459..de1ed11 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve",
+ "jsx": "react-jsx",
"incremental": true,
"plugins": [
{