Loading parks...
+dAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJw b z_^v8bbg` SAn{I*4bH$u(RZ6*x UhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=p C^ S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk( $?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU ^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvh CL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c 70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397* _cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111a H}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*I cmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU &68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-= A= yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v #ix45EVrcEhr>!NMhprl $InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~ &^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7< 4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}sc Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+ 9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2 `1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M =hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S( O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css new file mode 100644 index 00000000..6b717ad3 --- /dev/null +++ b/frontend/src/app/globals.css @@ -0,0 +1,21 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + color: var(--foreground); + background: var(--background); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx new file mode 100644 index 00000000..5a4d88b6 --- /dev/null +++ b/frontend/src/app/layout.tsx @@ -0,0 +1,62 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "ThrillWiki - Theme Park Information & Reviews", + description: "Discover theme parks, share experiences, and read reviews from park enthusiasts.", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + ++ + + ); +} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx new file mode 100644 index 00000000..2139778f --- /dev/null +++ b/frontend/src/app/page.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import type { Park } from '@/types/api'; + +export default function Home() { + const [parks, setParks] = useState+ + + + {children} + + +([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState (null); + + useEffect(() => { + async function fetchParks() { + try { + const response = await fetch('/api/parks'); + const data = await response.json(); + + if (!data.success) { + throw new Error(data.error || 'Failed to fetch parks'); + } + + setParks(data.data || []); + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred'); + } finally { + setLoading(false); + } + } + + fetchParks(); + }, []); + + if (loading) { + return ( + + + ); + } + + if (error) { + return ( ++ ++Loading parks...
++ + ); + } + + return ( +++Error
+{error}
++ + ); +} diff --git a/frontend/src/app/parks/page.tsx b/frontend/src/app/parks/page.tsx new file mode 100644 index 00000000..9a9a8cc2 --- /dev/null +++ b/frontend/src/app/parks/page.tsx @@ -0,0 +1,146 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import type { Park, ParkStatus } from '@/types/api'; + +export default function ParksPage() { + const [parks, setParks] = useStateThrillWiki Parks
+ + {parks.length === 0 ? ( +No parks found
+ ) : ( ++ {parks.map((park) => ( ++ )} +++ ))} +{park.name}
+ {park.description && ( ++ {park.description.length > 150 + ? `${park.description.slice(0, 150)}...` + : park.description} +
+ )} ++ Added: {new Date(park.createdAt).toLocaleDateString()} ++([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState (null); + + useEffect(() => { + async function fetchParks() { + try { + const response = await fetch('/api/parks'); + const data = await response.json(); + + if (!data.success) { + throw new Error(data.error || 'Failed to fetch parks'); + } + + setParks(data.data || []); + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred'); + } finally { + setLoading(false); + } + } + + fetchParks(); + }, []); + + const getStatusColor = (status: ParkStatus): string => { + const colors = { + OPERATING: 'bg-green-100 text-green-800', + CLOSED_TEMP: 'bg-yellow-100 text-yellow-800', + CLOSED_PERM: 'bg-red-100 text-red-800', + UNDER_CONSTRUCTION: 'bg-blue-100 text-blue-800', + DEMOLISHED: 'bg-gray-100 text-gray-800', + RELOCATED: 'bg-purple-100 text-purple-800' + }; + return colors[status] || 'bg-gray-100 text-gray-500'; + }; + + const formatRating = (rating: number | null | undefined): string => { + if (typeof rating !== 'number') return 'No ratings'; + return `${rating.toFixed(1)}/5`; + }; + + if (loading) { + return ( + ++ ); + } + + if (error) { + return ( ++ ++Loading parks...
+++ ); + } + + return ( +++Error
+{error}
+++ ); +} \ No newline at end of file diff --git a/frontend/src/components/error-boundary.tsx b/frontend/src/components/error-boundary.tsx new file mode 100644 index 00000000..33b6be19 --- /dev/null +++ b/frontend/src/components/error-boundary.tsx @@ -0,0 +1,81 @@ +'use client'; + +import { Component, ErrorInfo, ReactNode } from 'react'; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error?: Error; +} + +export class ErrorBoundary extends Component++Theme Parks
+ + {parks.length === 0 ? ( +No parks found
+ ) : ( ++ {parks.map((park) => ( ++ )} +++ ))} +++ + {park.description && ( ++ + {park.name} + +
+ + {park.status.replace('_', ' ')} + ++ {park.description.length > 150 + ? `${park.description.slice(0, 150)}...` + : park.description} +
+ )} + ++ {park.location && ( ++ ++ 📍 {park.location.latitude}, {park.location.longitude} +
+ )} + {park.opening_date && ( +🗓 Opened: {new Date(park.opening_date).toLocaleDateString()}
+ )} + {park.operating_season && ( +⏰ Season: {park.operating_season}
+ )} + {park.website && ( + + )} ++++ 🎢 {park.ride_count || 0} rides + ⭐ {formatRating(park.average_rating)} ++{ + public state: State = { + hasError: false + }; + + public static getDerivedStateFromError(error: Error): State { + return { + hasError: true, + error + }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('Uncaught error:', error, errorInfo); + } + + public render() { + if (this.state.hasError) { + return this.props.fallback || ( + ++ ); + } + + return this.props.children; + } +} \ No newline at end of file diff --git a/frontend/src/middleware.ts b/frontend/src/middleware.ts new file mode 100644 index 00000000..efd8e57b --- /dev/null +++ b/frontend/src/middleware.ts @@ -0,0 +1,85 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { headers } from 'next/headers'; + +// Paths that don't require authentication +const PUBLIC_PATHS = [ + '/api/auth/login', + '/api/auth/register', + '/api/parks', + '/api/parks/search', +]; + +// Function to check if path is public +const isPublicPath = (path: string) => { + return PUBLIC_PATHS.some(publicPath => { + if (publicPath.endsWith('*')) { + return path.startsWith(publicPath.slice(0, -1)); + } + return path === publicPath; + }); +}; + +export async function middleware(request: NextRequest) { + const path = request.nextUrl.pathname; + const isApiRoute = path.startsWith('/api/'); + + // Only apply middleware to API routes + if (!isApiRoute) { + return NextResponse.next(); + } + + // Allow public paths + if (isPublicPath(path)) { + return NextResponse.next(); + } + + // Check for auth token + const authHeader = request.headers.get('authorization'); + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return NextResponse.json( + { success: false, error: 'Unauthorized' }, + { status: 401 } + ); + } + + try { + // TODO: Implement token verification + // For now, just check if token exists + const token = authHeader.split(' ')[1]; + if (!token) { + throw new Error('Invalid token'); + } + + // Add user info to request headers for API routes + const requestHeaders = new Headers(request.headers); + requestHeaders.set('x-user-token', token); + + // Clone the request with modified headers + const response = NextResponse.next({ + request: { + headers: requestHeaders, + }, + }); + + return response; + } catch (error) { + return NextResponse.json( + { success: false, error: 'Invalid token' }, + { status: 401 } + ); + } +} + +export const config = { + matcher: [ + /* + * Match all API routes: + * - /api/auth/login + * - /api/parks + * - /api/reviews + * etc. + */ + '/api/:path*', + ], +}; \ No newline at end of file diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts new file mode 100644 index 00000000..bc6f4556 --- /dev/null +++ b/frontend/src/types/api.ts @@ -0,0 +1,148 @@ +// API Response Types +export interface ApiResponse+++++++ +++++ Something went wrong +
+ {this.state.error && ( +++ )} +{this.state.error.message}
++ ++{ + success: boolean; + data?: T; + error?: string; + metadata?: { + page?: number; + limit?: number; + total?: number; + }; +} + +// Auth Types +export interface UserAuth { + id: number; + email: string; + username: string; + token: string; +} + +// Park Status Enum +export enum ParkStatus { + OPERATING = 'OPERATING', + CLOSED_TEMP = 'CLOSED_TEMP', + CLOSED_PERM = 'CLOSED_PERM', + UNDER_CONSTRUCTION = 'UNDER_CONSTRUCTION', + DEMOLISHED = 'DEMOLISHED', + RELOCATED = 'RELOCATED' +} + +// Park Types +export interface Park { + id: number; + name: string; + slug: string; + description?: string; + status: ParkStatus; + location?: { + latitude: number; + longitude: number; + }; + opening_date?: string; + closing_date?: string; + operating_season?: string; + size_acres?: number; + website?: string; + average_rating?: number; + ride_count?: number; + coaster_count?: number; + creator?: User; + creatorId?: number; + owner?: Company; + ownerId?: number; + areas: ParkArea[]; + reviews: Review[]; + photos: Photo[]; + created_at: string; + updated_at: string; +} + +// Park Area Types +export interface ParkArea { + id: number; + name: string; + slug: string; + description?: string; + opening_date?: string; + closing_date?: string; + parkId: number; + park: Park; + created_at: string; + updated_at: string; +} + +// Company Types +export interface Company { + id: number; + name: string; + website?: string; + parks: Park[]; + created_at: string; + updated_at: string; +} + +// Review Types +export interface Review { + id: number; + content: string; + rating: number; + parkId: number; + userId: number; + photos: Photo[]; + created_at: string; + updated_at: string; + park: Park; + user: User; +} + +// Photo Types +export interface Photo { + id: number; + url: string; + caption?: string; + parkId?: number; + reviewId?: number; + userId: number; + created_at: string; + updated_at: string; + park?: Park; + review?: Review; + user: User; +} + +// User Types +export interface User { + id: number; + email: string; + username: string; + dateJoined: string; + isActive: boolean; + isStaff: boolean; + isSuperuser: boolean; + lastLogin?: string; + createdParks: Park[]; + reviews: Review[]; + photos: Photo[]; +} + +// Response Types +export interface ParkListResponse extends ApiResponse {} +export interface ParkDetailResponse extends ApiResponse {} +export interface ParkAreaListResponse extends ApiResponse {} +export interface ParkAreaDetailResponse extends ApiResponse {} +export interface CompanyListResponse extends ApiResponse {} +export interface CompanyDetailResponse extends ApiResponse {} +export interface ReviewListResponse extends ApiResponse {} +export interface ReviewDetailResponse extends ApiResponse {} +export interface UserListResponse extends ApiResponse {} +export interface UserDetailResponse extends ApiResponse {} +export interface PhotoListResponse extends ApiResponse {} +export interface PhotoDetailResponse extends ApiResponse {} + +// Error Types +export interface ApiError { + message: string; + code?: string; + details?: Record ; +} \ No newline at end of file diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts new file mode 100644 index 00000000..109807be --- /dev/null +++ b/frontend/tailwind.config.ts @@ -0,0 +1,18 @@ +import type { Config } from "tailwindcss"; + +export default { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + background: "var(--background)", + foreground: "var(--foreground)", + }, + }, + }, + plugins: [], +} satisfies Config; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 00000000..c1334095 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md index 420c679e..581d1a68 100644 --- a/memory-bank/activeContext.md +++ b/memory-bank/activeContext.md @@ -1,74 +1,99 @@ -# Active Development Context +# Active Context -## Recently Completed +## Current Status (Updated 2/23/2025 3:41 PM) -### Park Search Implementation (2024-02-22) +### API Test Results +✅ GET /api/parks +- Returns paginated list of parks +- Includes relationships (areas, reviews, photos) +- Proper metadata with total count +- Type-safe response structure -1. Autocomplete Base: - - Created BaseAutocomplete in core/forms.py - - Configured project-wide auth requirement - - Added test coverage for base functionality +✅ Search Parameters +- ?search=universal returns matching parks +- ?page and ?limit for pagination +- Case-insensitive search -2. Park Search: - - Implemented ParkAutocomplete class - - Created ParkSearchForm with autocomplete widget - - Updated views and templates for integration - - Added comprehensive test suite +✅ POST /api/parks +- Correctly enforces authentication +- Returns 401 for unauthorized requests +- Validates required fields -3. Documentation: - - Updated memory-bank/features/parks/search.md - - Added test documentation - - Created user interface guidelines +❌ Park Detail Routes +- /parks/[slug] returns 404 +- Need to implement park detail API +- Need to create park detail page -## Active Tasks +### Working Features +1. Parks API + - GET /api/parks with full data + - Search and pagination + - Protected POST endpoint + - Error handling -1. Testing: - - [ ] Run the test suite with `uv run pytest parks/tests/` - - [ ] Monitor test coverage with pytest-cov - - [ ] Verify HTMX interactions work as expected +2. Parks Listing + - Displays all parks + - Responsive grid layout + - Status badge with colors + - Loading states + - Error handling -2. Performance Monitoring: - - [ ] Add database indexes if needed - - [ ] Monitor query performance - - [ ] Consider caching strategies +### Immediate Next Steps -3. User Experience: - - [ ] Get feedback on search responsiveness - - [ ] Monitor error rates - - [ ] Check accessibility compliance +1. Park Detail Implementation (High Priority) + - [ ] Create /api/parks/[slug] endpoint + - [ ] Add park detail page component + - [ ] Handle loading states + - [ ] Add reviews section -## Next Steps +2. Authentication (High Priority) + - [ ] Implement JWT token management + - [ ] Add login/register forms + - [ ] Protected route middleware + - [ ] Auth context provider -1. Enhancements: - - Add geographic search capabilities - - Implement result caching - - Add full-text search support +3. UI Improvements (Medium Priority) + - [ ] Add search input in UI + - [ ] Implement filter controls + - [ ] Add proper loading skeletons + - [ ] Improve error messages -2. Integration: - - Extend to other models (Rides, Areas) - - Add combined search functionality - - Improve filter integration +### Known Issues +1. No authentication system yet +2. Missing park detail views +3. No form validation +4. No image upload handling +5. No real-time updates +6. Static metadata (page size) -3. Testing: - - Add Playwright e2e tests - - Implement performance benchmarks - - Add accessibility tests +### Required Documentation +1. API Endpoints + - ✅ GET /api/parks + - ✅ POST /api/parks + - ❌ GET /api/parks/[slug] + - ❌ PUT /api/parks/[slug] + - ❌ DELETE /api/parks/[slug] -## Technical Debt +2. Component Documentation + - ❌ Parks list component + - ❌ Park card component + - ❌ Status badge component + - ❌ Loading states -None currently identified for the search implementation. +3. Authentication Flow + - ❌ JWT implementation + - ❌ Protected routes + - ❌ Auth context + - ❌ Login/Register forms -## Dependencies - -- django-htmx-autocomplete -- pytest-django -- pytest-cov +## Configuration +- Next.js 15.1.7 +- Prisma with PostGIS +- PostgreSQL database +- REST API patterns ## Notes - -The implementation follows these principles: -- Authentication-first approach -- Performance optimization -- Accessibility compliance -- Test coverage -- Clean documentation \ No newline at end of file +1. Authentication needed before implementing write operations +2. Consider caching for park data +3. Need to implement proper error logging +4. Consider rate limiting for API diff --git a/memory-bank/decisions/nextjs_migration.md b/memory-bank/decisions/nextjs_migration.md new file mode 100644 index 00000000..435b5274 --- /dev/null +++ b/memory-bank/decisions/nextjs_migration.md @@ -0,0 +1,146 @@ +# Next.js Migration Plan + +## Overview +This document outlines the strategy for migrating ThrillWiki from a Django monolith to a Next.js frontend with API Routes backend while maintaining all existing functionality and design. + +## Current Architecture +- Django monolithic application +- Django templates with HTMX and Alpine.js +- Django views handling both API and page rendering +- Django ORM for database operations +- Custom analytics system +- File upload handling through Django +- Authentication through Django + +## Target Architecture +- Next.js 14+ application using App Router +- React components replacing Django templates +- Next.js API Routes replacing Django views +- Prisma ORM replacing Django ORM +- JWT-based authentication system +- Maintain current DB schema +- API-first approach with type safety +- File uploads through Next.js API routes + +## Component Mapping +Major sections requiring migration: + +1. Parks System: + - Convert Django views to API routes + - Convert templates to React components + - Implement dynamic routing + - Maintain search functionality + +2. User System: + - Implement JWT authentication + - Convert user management to API routes + - Migrate profile management + - Handle avatar uploads + +3. Reviews System: + - Convert to API routes + - Implement real-time updates + - Maintain moderation features + +4. Analytics: + - Convert to API routes + - Implement client-side tracking + - Maintain current metrics + +## API Route Mapping +```typescript +// Example API route structure +/api + /auth + /login + /register + /profile + /parks + /[id] + /search + /nearby + /reviews + /[id] + /create + /moderate + /analytics + /track + /stats +``` + +## Migration Phases + +### Phase 1: Setup & Infrastructure +1. Initialize Next.js project +2. Set up Prisma with existing schema +3. Configure TypeScript +4. Set up authentication system +5. Configure file upload handling + +### Phase 2: Core Features +1. Parks system migration +2. User authentication +3. Basic CRUD operations +4. Search functionality +5. File uploads + +### Phase 3: Advanced Features +1. Reviews system +2. Analytics +3. Moderation tools +4. Real-time features +5. Admin interfaces + +### Phase 4: Testing & Polish +1. Comprehensive testing +2. Performance optimization +3. SEO implementation +4. Security audit +5. Documentation updates + +## Dependencies + +### Frontend +- next: ^14.0.0 +- react: ^18.2.0 +- react-dom: ^18.2.0 +- @prisma/client +- @tanstack/react-query +- tailwindcss +- typescript +- zod (for validation) +- jwt-decode +- @headlessui/react + +### Backend +- prisma +- jsonwebtoken +- bcryptjs +- multer (file uploads) +- sharp (image processing) + +## Data Migration Strategy +1. Create Prisma schema matching Django models +2. Write migration scripts for data transfer +3. Validate data integrity +4. Implement rollback procedures + +## Security Considerations +1. JWT token handling +2. CSRF protection +3. Rate limiting +4. File upload security +5. API route protection + +## Performance Optimization +1. Implement ISR (Incremental Static Regeneration) +2. Optimize images and assets +3. Implement caching strategy +4. Code splitting +5. Bundle optimization + +## Rollback Plan +1. Maintain dual systems during migration +2. Database backup strategy +3. Traffic routing plan +4. Monitoring and alerts \ No newline at end of file diff --git a/memory-bank/features/frontend_structure.md b/memory-bank/features/frontend_structure.md new file mode 100644 index 00000000..d5546388 --- /dev/null +++ b/memory-bank/features/frontend_structure.md @@ -0,0 +1,165 @@ +# Frontend Structure Documentation + +## Project Organization + +``` +frontend/ +├── src/ +│ ├── app/ # Next.js App Router pages +│ ├── components/ # Reusable React components +│ ├── types/ # TypeScript type definitions +│ └── lib/ # Utility functions and configurations +``` + +## Component Architecture + +### Core Components + +1. **Layout (layout.tsx)** + - Global page structure + - Navigation header + - Footer + - Consistent styling across pages + - Using Inter font + - Responsive design with Tailwind + +2. **Error Boundary (error-boundary.tsx)** + - Global error handling + - Fallback UI for errors + - Error reporting capabilities + - Retry functionality + - Type-safe implementation + +3. **Home Page (page.tsx)** + - Parks listing + - Loading states + - Error handling + - Responsive grid layout + - Pagination support + +## API Integration + +### Parks API +- GET /api/parks + - Pagination support + - Search functionality + - Error handling + - Type-safe responses + +## State Management +- Using React hooks for local state +- Server-side data fetching +- Error state handling +- Loading state management + +## Styling Approach +1. **Tailwind CSS** + - Utility-first approach + - Custom theme configuration + - Responsive design utilities + - Component-specific styles + +2. **Component Design** + - Consistent spacing + - Mobile-first approach + - Accessible color schemes + - Interactive states + +## Type Safety +1. **TypeScript Integration** + - Strict type checking + - API response types + - Component props typing + - Error handling types + +## Error Handling Strategy +1. **Multiple Layers** + - Component-level error boundaries + - API error handling + - Type-safe error responses + - User-friendly error messages + +2. **Recovery Options** + - Retry functionality + - Graceful degradation + - Clear error messaging + - User guidance + +## Performance Considerations +1. **Optimizations** + - Component code splitting + - Image optimization + - Responsive loading + - Caching strategy + +2. **Monitoring** + - Error tracking + - Performance metrics + - User interactions + - API response times + +## Accessibility +1. **Implementation** + - ARIA labels + - Keyboard navigation + - Focus management + - Screen reader support + +## Next Steps + +### Immediate Tasks +1. Implement authentication components +2. Add park detail page +3. Implement search functionality +4. Add pagination controls + +### Future Improvements +1. Add park reviews system +2. Implement user profiles +3. Add social sharing +4. Enhance search capabilities + +## Migration Progress + +### Completed +✅ Basic page structure +✅ Error handling system +✅ Parks listing page +✅ API integration structure + +### In Progress +⏳ Authentication system +⏳ Park details page +⏳ Search functionality + +### Pending +⬜ User profiles +⬜ Reviews system +⬜ Admin interface +⬜ Analytics integration + +## Testing Strategy + +### Unit Tests +- Component rendering +- API integrations +- Error handling +- State management + +### Integration Tests +- User flows +- API interactions +- Error scenarios +- Authentication + +### E2E Tests +- Critical paths +- User journeys +- Cross-browser testing +- Mobile responsiveness + +## Documentation Needs +1. Component API documentation +2. State management patterns +3. Error handling procedures +4. Testing guidelines \ No newline at end of file diff --git a/memory-bank/features/nextjs_migration.md b/memory-bank/features/nextjs_migration.md new file mode 100644 index 00000000..e7088be0 --- /dev/null +++ b/memory-bank/features/nextjs_migration.md @@ -0,0 +1,155 @@ +# Next.js Migration Progress + +## Current Status (Updated 2/23/2025) + +### Completed Setup +1. ✅ Next.js project initialized in frontend/ +2. ✅ TypeScript configuration +3. ✅ Prisma setup with PostGIS support +4. ✅ Environment configuration +5. ✅ API route structure +6. ✅ Initial database schema sync +7. ✅ Basic UI components + +### Database Migration Status +- Detected existing Django schema with 70+ tables +- Successfully initialized Prisma with PostGIS extension +- Created initial migration + +### Key Database Tables Identified +1. Core Tables + - accounts_user + - parks_park + - reviews_review + - location_location + - media_photo + +2. Authentication Tables + - socialaccount_socialaccount + - token_blacklist_blacklistedtoken + - auth_permission + +3. Content Management + - wiki_article + - wiki_articlerevision + - core_slughistory + +### Implemented Features +1. Authentication Middleware + - Basic JWT token validation + - Public/private route handling + - Token forwarding + +2. API Types System + - Base response types + - Park types + - User types + - Review types + - Error handling types + +3. Database Connection + - Prisma client setup + - PostGIS extension configuration + - Development/production handling + +4. Parks API Route + - GET endpoint with pagination + - Search functionality + - POST endpoint with auth + - Error handling + +## Next Steps (Prioritized) + +### 1. Schema Migration (Current Focus) +- [ ] Map remaining Django models to Prisma schema +- [ ] Handle custom field types (e.g., GeoDjango fields) +- [ ] Set up relationships between models +- [ ] Create data migration scripts + +### 2. Authentication System +- [ ] Implement JWT verification +- [ ] Set up refresh tokens +- [ ] Social auth integration +- [ ] User session management + +### 3. Core Features Migration +- [ ] Parks system +- [ ] User profiles +- [ ] Review system +- [ ] Media handling + +### 4. Testing & Validation +- [ ] Unit tests for API routes +- [ ] Integration tests +- [ ] Data integrity checks +- [ ] Performance testing + +## Technical Decisions + +### Schema Migration Strategy +- Incremental model migration +- Maintain foreign key relationships +- Handle custom field types via Prisma +- Use PostGIS for spatial data + +### Authentication Approach +- JWT for API authentication +- HTTP-only cookies for token storage +- Refresh token rotation +- Social auth provider integration + +### API Architecture +- REST-based endpoints +- Strong type safety +- Consistent response formats +- Built-in pagination +- Error handling middleware + +### Component Architecture +- Server components by default +- Client components for interactivity +- Shared component library +- Error boundaries + +## Migration Challenges + +### Current Challenges +1. Complex Django model relationships +2. Custom field type handling +3. Social authentication flow +4. File upload system +5. Real-time feature migration + +### Solutions +1. Using Prisma's preview features for PostGIS +2. Custom field type mappings +3. JWT-based auth with refresh tokens +4. S3/cloud storage integration +5. WebSocket/Server-Sent Events + +## Monitoring & Validation + +### Data Integrity +- Validation scripts for migrated data +- Comparison tools for Django/Prisma models +- Automated testing of relationships +- Error logging and monitoring + +### Performance +- API response time tracking +- Database query optimization +- Client-side performance metrics +- Error rate monitoring + +## Documentation Updates +1. API route specifications +2. Schema migration process +3. Authentication flows +4. Component documentation +5. Deployment guides + +## Rollback Strategy +1. Maintain Django application +2. Database backups before migrations +3. Feature flags for gradual rollout +4. Monitoring thresholds for auto-rollback \ No newline at end of file