Skip to content

Commit

Permalink
Update event example.
Browse files Browse the repository at this point in the history
  • Loading branch information
daviddaytw committed Sep 11, 2024
1 parent 9dea297 commit 51722ad
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 19 deletions.
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SUPABASE_URL=http://127.0.0.1:54321
SUPABASE_ANON_KEY=
VITE_SUPABASE_URL=http://127.0.0.1:54321
VITE_SUPABASE_ANON_KEY=
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ Tailwind 的配置檔位於 `tailwind.config.js`。
要與 Supabase 資料庫互動,你需要設置 `.env.local` 檔案,並填入以下變數:

```
SUPABASE_URL=your-supabase-url
SUPABASE_ANON_KEY=your-supabase-key
VITE_SUPABASE_URL=your-supabase-url
VITE_SUPABASE_ANON_KEY=your-supabase-key
```

### Edge Functions
Expand Down
13 changes: 13 additions & 0 deletions src/controllers/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { supabase } from "../utils/supabase";

export const UserController = {
async get() {
const { data, error } = await supabase.auth.getUser()

if( error !== null ) {
throw error
}

return data.user
}
}
28 changes: 25 additions & 3 deletions src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@
// Import Routes

import { Route as rootRoute } from './routes/__root'
import { Route as LoginImport } from './routes/login'
import { Route as EventsIndexImport } from './routes/events/index'
import { Route as EventsCreateImport } from './routes/events/create'
import { Route as EventsEventIdImport } from './routes/events/$eventId'

// Create/Update Routes

const LoginRoute = LoginImport.update({
path: '/login',
getParentRoute: () => rootRoute,
} as any)

const EventsIndexRoute = EventsIndexImport.update({
path: '/events/',
getParentRoute: () => rootRoute,
Expand All @@ -36,6 +42,13 @@ const EventsEventIdRoute = EventsEventIdImport.update({

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/login': {
id: '/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof LoginImport
parentRoute: typeof rootRoute
}
'/events/$eventId': {
id: '/events/$eventId'
path: '/events/$eventId'
Expand Down Expand Up @@ -63,39 +76,44 @@ declare module '@tanstack/react-router' {
// Create and export the route tree

interface FileRoutesByFullPath {
'/login': typeof LoginRoute
'/events/$eventId': typeof EventsEventIdRoute
'/events/create': typeof EventsCreateRoute
'/events': typeof EventsIndexRoute
}

interface FileRoutesByTo {
'/login': typeof LoginRoute
'/events/$eventId': typeof EventsEventIdRoute
'/events/create': typeof EventsCreateRoute
'/events': typeof EventsIndexRoute
}

interface FileRoutesById {
'/login': typeof LoginRoute
'/events/$eventId': typeof EventsEventIdRoute
'/events/create': typeof EventsCreateRoute
'/events/': typeof EventsIndexRoute
}

interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/events/$eventId' | '/events/create' | '/events'
fullPaths: '/login' | '/events/$eventId' | '/events/create' | '/events'
fileRoutesByTo: FileRoutesByTo
to: '/events/$eventId' | '/events/create' | '/events'
id: '/events/$eventId' | '/events/create' | '/events/'
to: '/login' | '/events/$eventId' | '/events/create' | '/events'
id: '/login' | '/events/$eventId' | '/events/create' | '/events/'
fileRoutesById: FileRoutesById
}

interface RootRouteChildren {
LoginRoute: typeof LoginRoute
EventsEventIdRoute: typeof EventsEventIdRoute
EventsCreateRoute: typeof EventsCreateRoute
EventsIndexRoute: typeof EventsIndexRoute
}

const rootRouteChildren: RootRouteChildren = {
LoginRoute: LoginRoute,
EventsEventIdRoute: EventsEventIdRoute,
EventsCreateRoute: EventsCreateRoute,
EventsIndexRoute: EventsIndexRoute,
Expand All @@ -113,11 +131,15 @@ export const routeTree = rootRoute
"__root__": {
"filePath": "__root.tsx",
"children": [
"/login",
"/events/$eventId",
"/events/create",
"/events/"
]
},
"/login": {
"filePath": "login.tsx"
},
"/events/$eventId": {
"filePath": "events/$eventId.tsx"
},
Expand Down
26 changes: 23 additions & 3 deletions src/routes/events/$eventId.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { createFileRoute, useParams } from '@tanstack/react-router';
import { createFileRoute } from '@tanstack/react-router';
import { supabase } from '../../utils/supabase';

export const Route = createFileRoute('/events/$eventId')({
loader: async ({ params: { eventId } }) => {
const { data, error } = await supabase
.from('events')
.select('*')
.eq('id', eventId)
.single()

if (error !== null) {
throw error
}

return { event: data }
},
component: EventDetails
})

function EventDetails() {
const { eventId } = useParams({ strict: false })
return <div>Event {eventId}</div>
const { event } = Route.useLoaderData()
return (
<div>
<div>Event ID: {event.id}</div>
<div>Event Name: {event.name}</div>
<div>Event Description: {event.description}</div>
</div>
)
}
42 changes: 38 additions & 4 deletions src/routes/events/create.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { createFileRoute, Link } from '@tanstack/react-router';
import { ChangeEvent, useEffect, useState } from 'react';
import { ChangeEvent, FormEvent, useEffect, useState } from 'react';
import { UserController } from '../../controllers/user';
import { AuthGuard } from '../../utils/auth';
import { supabase } from '../../utils/supabase';

export const Route = createFileRoute('/events/create')({
beforeLoad: AuthGuard,
component: CreateEventScreen
})

Expand All @@ -25,8 +29,12 @@ const styles = {
}

function CreateEventScreen() {
const navigate = Route.useNavigate()
const [selectedPhotos, setSelectedPhotos] = useState<File>()
const [preview, setPreview] = useState<string>()
const [inputs, setInputs] = useState({
name: '',
})

// create a preview as a side effect, whenever selected file is changed
useEffect(() => {
Expand All @@ -50,8 +58,32 @@ function CreateEventScreen() {
setSelectedPhotos(e.target.files[0])
}


async function addEvent(e: FormEvent) {
e.preventDefault()
const { data, error } = await supabase
.from('events')
.insert({
...inputs,
user_id: (await UserController.get()).id
})
.select('*')
.single()

if (error !== null) {
throw error
}

navigate({
to: '/events/$eventId',
params: {
'eventId': data.id.toString()
}
})
}

return (
<div style={styles.container}>
<form style={styles.container} onSubmit={addEvent}>
<div className='flex'>
<Link to="/events">
<button className='ms-2 text-white'>返回</button>
Expand All @@ -64,6 +96,8 @@ function CreateEventScreen() {
style={styles.input}
className='rounded'
placeholder="請輸入活動名稱"
value={inputs.name}
onChange={(text) => { setInputs({ ...inputs, name: text.target.value }) }}
/>
<div className="flex gap-3 mt-3 ms-4">
<p style={styles.text}>開始時間</p>
Expand Down Expand Up @@ -115,8 +149,8 @@ function CreateEventScreen() {
)}
</div>
<div className='mt-3'>
<button className='text-white'>確認新增</button>
<button className='text-white' type='submit'>確認新增</button>
</div>
</div>
</form>
);
}
28 changes: 25 additions & 3 deletions src/routes/events/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createFileRoute } from '@tanstack/react-router';
import { createFileRoute, Link } from '@tanstack/react-router';
import { supabase } from '../../utils/supabase';

const styles = {
container: {
Expand All @@ -8,9 +9,30 @@ const styles = {
};

export const Route = createFileRoute('/events/')({
component: () => (
loader: async () => {
const { data, error } = await supabase
.from('events')
.select('*')

if (error !== null) {
throw error
}

return { events: data }
},
component: EventIndex
})

function EventIndex() {
const { events } = Route.useLoaderData()
return (
<div style={styles.container}>
<h1 style={{ marginLeft: 140 }} className='text-lg text-white'>活動列表</h1>
{
events.map((event) => (
<Link to='/events/$eventId' params={{ eventId: event.id.toString() }} >{event.name}</Link>
))
}
</div>
)
})
}
44 changes: 44 additions & 0 deletions src/routes/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createFileRoute } from '@tanstack/react-router'
import { useState } from 'react'
import { supabase } from '../utils/supabase'

export const Route = createFileRoute('/login')({
component: LoginPage,
validateSearch: (search: Record<string, unknown>) => {
return {
redirect: (search.redirect as string) || '/',
}
},
})

function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const { redirect: redirectUrl } = Route.useSearch()

async function login() {
const { data: { session }, error } = await supabase.auth.signInWithPassword({
email,
password
})

if (error !== null) {
throw error
}

if (session !== null) {
window.location.href = redirectUrl
}
}

return (
<div>
<label>Email: </label>
<input className='border' type="email" onChange={(e) => { setEmail(e.target.value) }} />
<label>Password:</label>
<input className='border' type="password" onChange={(e) => { setPassword(e.target.value) }} />
<button onClick={login}>Login</button>
</div>
)

}
22 changes: 22 additions & 0 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ParsedLocation, redirect } from "@tanstack/react-router";
import { supabase } from "./supabase";

export async function AuthGuard({ location }: { location: ParsedLocation }) {
const { data: { session }, error } = await supabase.auth.getSession()

if( error !== null ) {
throw error
}

if (session === null) {
throw redirect({
to: '/login',
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.href,
},
})
}
}
4 changes: 2 additions & 2 deletions src/utils/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { Database } from './database.types'

// supabase client for interacting with the database
export const supabase = createClient<Database>(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
)

0 comments on commit 51722ad

Please sign in to comment.