Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
VITE_MEILISEARCH_HOST=https://search.bettergov.ph
VITE_MEILISEARCH_PORT=443
VITE_MEILISEARCH_SEARCH_API_KEY= # Meilisearch Search API Key
VITE_MEILISEARCH_SEARCH_API_KEY= # Meilisearch Search API Key
VITE_MAILER_API=https://better-gov-mailer.onrender.com
775 changes: 591 additions & 184 deletions package-lock.json

Large diffs are not rendered by default.

18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "vite",
"build": "tsc && npm run generate:metadata && vite build",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write .",
"format": "prettier --write ./src/pages/philippines",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed",
Expand All @@ -34,14 +34,20 @@
"generate:metadata": "npm run generate:llms-txt && npm run generate:sitemap"
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@icons-pack/react-simple-icons": "^13.7.0",
"@meilisearch/instant-meilisearch": "^0.26.0",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/react-leaflet": "^3.0.0",
"clsx": "^2.1.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"csv-parser": "^3.2.0",
"date-fns": "^3.3.1",
"dotenv": "^16.5.0",
Expand All @@ -53,17 +59,21 @@
"leaflet": "^1.9.4",
"lucide-react": "^0.513.0",
"meilisearch": "^0.50.0",
"next-themes": "^0.4.6",
"nuqs": "^2.6.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.63.0",
"react-i18next": "^15.7.3",
"react-instantsearch": "^7.15.8",
"react-leaflet": "5.0.0-rc.2",
"react-router-dom": "^6.22.2",
"react-world-flags": "^1.5.0",
"recharts": "^2.15.3",
"tailwind-merge": "^2.2.1",
"sonner": "^2.0.7",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"wrangler": "^4.19.1",
"zod": "^4.1.11"
},
Expand Down
31 changes: 19 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NuqsAdapter } from 'nuqs/adapters/react-router/v6';
import Navbar from './components/layout/Navbar';
import Ticker from './components/ui/Ticker';
import Footer from './components/layout/Footer';
import Toaster from './components/ui/sonner';
import Home from './pages/Home';
import DesignGuide from './pages/DesignGuide';
import Services from './pages/services';
Expand All @@ -20,7 +21,7 @@ import PhilippinesCulture from './pages/philippines/culture';
import PhilippinesRegions from './pages/philippines/regions';
import PhilippinesMap from './pages/philippines/map';
import PublicHolidays from './pages/philippines/holidays';
import Hotlines from './pages/philippines/Hotlines';
import Hotlines from './pages/philippines/hotline';
import VisaPage from './pages/travel/visa';
import VisaTypesPage from './pages/travel/visa-types';
import VisaTypeDetail from './pages/travel/visa-types/[type]';
Expand Down Expand Up @@ -92,6 +93,7 @@ function App() {
<div className='min-h-screen flex flex-col'>
<Navbar />
<Ticker />
<Toaster position='top-right' closeButton />
<ScrollToTop />
<Routes>
<Route path='/' element={<Home />} />
Expand All @@ -105,17 +107,22 @@ function App() {
<Route path='/terms-of-service' element={<TermsOfService />} />
<Route path='/sitemap' element={<SitemapPage />} />
<Route path='/discord' Component={Discord} />

<Route path='/philippines'>
<Route index element={<Navigate to='about' replace />} />
<Route path='about' element={<AboutPhilippines />} />
<Route path='history' element={<PhilippinesHistory />} />
<Route path='culture' element={<PhilippinesCulture />} />
<Route path='regions' element={<PhilippinesRegions />} />
<Route path='map' element={<PhilippinesMap />} />
<Route path='holidays' element={<PublicHolidays />} />
<Route path='hotlines' element={<Hotlines />} />
</Route>
<Route path='/philippines/about' element={<AboutPhilippines />} />
<Route
path='/philippines/history'
element={<PhilippinesHistory />}
/>
<Route
path='/philippines/culture'
element={<PhilippinesCulture />}
/>
<Route
path='/philippines/regions'
element={<PhilippinesRegions />}
/>
<Route path='/philippines/map' element={<PhilippinesMap />} />
<Route path='/philippines/holidays' element={<PublicHolidays />} />
<Route path='/philippines/hotlines' element={<Hotlines />} />

{/* Data Routes */}
<Route path='/data/weather' element={<WeatherPage />} />
Expand Down
103 changes: 103 additions & 0 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import * as React from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { X } from 'lucide-react';

const Dialog = DialogPrimitive.Root;

const DialogTrigger = DialogPrimitive.Trigger;

const DialogPortal = DialogPrimitive.Portal;

const DialogClose = DialogPrimitive.Close;

const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={`fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 ${className}`}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;

const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={`fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg ${className}`}
{...props}
>
{children}
<DialogPrimitive.Close className='absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500'>
<X className='h-4 w-4' />
<span className='sr-only'>Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={`flex flex-col space-y-1.5 text-center sm:text-left ${className}`}
{...props}
/>
);
DialogHeader.displayName = 'DialogHeader';

const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={`flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 ${className}`}
{...props}
/>
);
DialogFooter.displayName = 'DialogFooter';

const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={`text-lg font-semibold leading-none tracking-tight ${className}`}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;

const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={`text-sm text-gray-500 ${className}`}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;

export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};
23 changes: 23 additions & 0 deletions src/components/ui/form/constant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { type FieldPath, type FieldValues } from 'react-hook-form';

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
);

type FormItemContextValue = {
id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
);

export { FormFieldContext, FormItemContext };
28 changes: 28 additions & 0 deletions src/components/ui/form/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import { useFormContext } from 'react-hook-form';
import { FormFieldContext, FormItemContext } from './constant';

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};

export { useFormField };
Loading
Loading