Skip to content

Commit

Permalink
Merge branch 'NCUAppTeam:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Elaine2899 authored Nov 30, 2024
2 parents cda97ba + f00464a commit d5ebb7b
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 46 deletions.
2 changes: 0 additions & 2 deletions .env.example

This file was deleted.

12 changes: 12 additions & 0 deletions src/components/icons/ClockIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import React from 'react';
import IconProps from '../interface/IconProps';
import { BasicIcon } from './BasicIcon';

export const ClockIcon: React.FC<IconProps> = ({ fill, stroke, size }) => (
<BasicIcon fill={fill} stroke={stroke} size={size}>
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M12 8v4l3 3m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</BasicIcon>
);

export default ClockIcon;
12 changes: 12 additions & 0 deletions src/components/icons/PinIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import React from 'react';
import IconProps from '../interface/IconProps';
import { BasicIcon } from './BasicIcon';

export const PinIcon: React.FC<IconProps> = ({ fill, stroke, size }) => (
<BasicIcon fill={fill} stroke={stroke} size={size}>
<path fill-rule="evenodd" d="m7.539 14.841.003.003.002.002a.755.755 0 0 0 .912 0l.002-.002.003-.003.012-.009a5.57 5.57 0 0 0 .19-.153 15.588 15.588 0 0 0 2.046-2.082c1.101-1.362 2.291-3.342 2.291-5.597A5 5 0 0 0 3 7c0 2.255 1.19 4.235 2.292 5.597a15.591 15.591 0 0 0 2.046 2.082 8.916 8.916 0 0 0 .189.153l.012.01ZM8 8.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" clip-rule="evenodd" />
</BasicIcon>
);

export default PinIcon;
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export { Header } from './Header';
// Icons
export { BellIcon } from './icons/BellIcon';
export { CalendarIcon } from './icons/CalendarIcon';
export { ClockIcon } from './icons/ClockIcon';
export { LogoutIcon } from './icons/LogoutIcon';
export { PinIcon } from './icons/PinIcon';
export { PlusIcon } from './icons/PlusIcon';
export { SidebarArrowDownIcon } from './icons/SidebarArrowDownIcon';
export { SidebarArrowRightIcon } from './icons/SidebarArrowRightIcon';
Expand Down
15 changes: 15 additions & 0 deletions src/routes/events/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ function CreateEventScreen() {
const [preview, setPreview] = useState<string>()
const [inputs, setInputs] = useState({
name: '',
start_time: '',
end_time: '',
location: '',
fee: 0,
description: ''
})

// create a preview as a side effect, whenever selected file is changed
Expand Down Expand Up @@ -106,6 +111,8 @@ function CreateEventScreen() {
type="datetime-local"
id="start-time"
name="start-time"
value={inputs.start_time}
onChange={(text) => { setInputs({ ...inputs, start_time: text.target.value }) }}
/>
</div>
<div className="flex gap-3 mt-3 ms-4">
Expand All @@ -114,25 +121,33 @@ function CreateEventScreen() {
type="datetime-local"
id="end-time"
name="end-time"
value={inputs.end_time}
onChange={(text) => { setInputs({ ...inputs, end_time: text.target.value }) }}
/>
</div>
<p style={styles.text}>活動地點</p>
<input
style={styles.input}
className="rounded"
placeholder="請輸入活動地點"
value={inputs.location}
onChange={(text) => { setInputs({ ...inputs, location: text.target.value }) }}
/>
<p style={styles.text}>參加費用</p>
<input
style={styles.input}
className="rounded"
placeholder="請輸入參加費用(請輸入數字,無則填0)"
value={inputs.fee}
onChange={(text) => { setInputs({ ...inputs, fee: Number(text.target.value) }) }}
/>
<p style={styles.text}>活動介紹</p>
<input
style={styles.input}
className="rounded"
placeholder="請介紹你的活動"
value={inputs.description}
onChange={(text) => { setInputs({ ...inputs, description: text.target.value }) }}
/>
</div>
<div className='mt-3 ms-2'>
Expand Down
96 changes: 75 additions & 21 deletions src/routes/events/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { createFileRoute, Link } from '@tanstack/react-router';
import { BellIcon, Header, PlusIcon } from '../../components';
import { createFileRoute } from '@tanstack/react-router';
import { Clock } from "flowbite-react-icons/solid";
import { BellIcon, Header, PinIcon, PlusIcon } from '../../components';
import { AuthGuard } from '../../utils/auth';
import { supabase } from '../../utils/supabase';
interface Event {
created_at: string;
description: string | null;
end_time: string | null;
fee: number | null;
id: number;
name: string | null;
start_time: string | null;
type: number | null;
user_id: string;
location: string | null;
}

const styles = {
container: {
flex: 1,
backgroundColor: '#333333',
},
};
export const Route = createFileRoute('/events/')({
beforeLoad: AuthGuard,
loader: async () => {
Expand All @@ -19,7 +26,6 @@ export const Route = createFileRoute('/events/')({
if (error !== null) {
throw error
}

return { events: data }
},
component: EventIndex
Expand All @@ -28,23 +34,41 @@ export const Route = createFileRoute('/events/')({
function EventIndex() {
const { events } = Route.useLoaderData()
const navigate = Route.useNavigate();

// console.log(events[0].start_time)

return (
<>
<div className="container mx-auto">
<Header />
<div style={styles.container}>
<h1 style={{ marginLeft: 140 }} className='text-lg text-white'>活動列表</h1>
{
events.map((event) => (
<Link key={event.id} to='/events/$eventId' params={{ eventId: event.id.toString() }} >{event.name}</Link>
))
}
<div className="flex-1 bg-gray-800 p-4">
<h1 className="ml-4 text-xl text-white">最新揪人</h1>
<div className="overflow-x-auto mt-2">
<div className="flex space-x-4">
{events.map((event) => (
<EventCard key={event.id} event={event} />
))}
</div>
</div>

<h1 className="ml-4 text-xl text-white mt-8">最新活動</h1>
<div className="overflow-x-auto mt-2">
<div className="flex space-x-4">
{events.map((event) => (
<EventCard key={event.id} event={event} />
))}
</div>
</div>
</div>
<button className="btn btn-circle fixed right-4 bottom-4" onClick={()=>{
if(document){
(document.getElementById('my_modal_4') as HTMLFormElement).showModal();

<button
className="btn btn-circle fixed right-4 bottom-4"
onClick={() => {
if (document) {
(document.getElementById('my_modal_4') as HTMLFormElement).showModal();
}
}}>
}}
>
<PlusIcon />
<dialog id="my_modal_4" className="modal">
<div className="modal-box w-11/12 max-w-5xl">
Expand All @@ -54,7 +78,12 @@ function EventIndex() {
</div>
<p className="py-4 text-xl">確定要新增嗎?</p>
<div className="modal-action flex justify-between">
<button className="btn w-1/2" onClick={() => navigate({ to: '/events/create' })}></button>
<button
className="btn w-1/2"
onClick={() => navigate({ to: '/events/create' })}
>
</button>
<form method="dialog" className="w-1/2">
{/* This button will close the dialog */}
<button className="btn w-full">取消</button>
Expand All @@ -67,3 +96,28 @@ function EventIndex() {
</>
)
}

function EventCard({ event }: { event: Event }) {
const startTime = event.start_time ? new Date(event.start_time) : new Date();
return (
<div className="flex-shrink-0 w-40 bg-gray-700 rounded-lg overflow-hidden text-white">
<div className="h-32 bg-gray-500" />
<div className="p-2">
<h3 className="text-lg mb-1">{event.name}</h3>
<p className="text-sm flex items-center">
<Clock fill="currentColor" stroke="#ffffff" size={24} />
{startTime.toLocaleString('zh-TW', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
})}
</p>
<p className="text-sm flex items-center">
<PinIcon fill="currentColor" stroke="#ffffff" size={24} />
{event.location || '位置未提供'}
</p>
</div>
</div>
);
}
95 changes: 72 additions & 23 deletions src/routes/login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createFileRoute } from '@tanstack/react-router'
import { useState } from 'react'
import { VStack } from '../components'
import { supabase } from '../utils/supabase'
import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';
import { supabase } from '../utils/supabase';

export const Route = createFileRoute('/login')({
component: LoginPage,
Expand All @@ -15,16 +14,17 @@ export const Route = createFileRoute('/login')({
function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isRemember, setIsRemember] = useState(false);
const [isError, setIsError] = useState(false);
const { redirect: redirectUrl } = Route.useSearch()

async function login() {
const { data: { session }, error } = await supabase.auth.signInWithPassword({
email,
password
})
async function login(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const { data: { session }, error } = await supabase.auth.signInWithPassword({ email, password });

if (error !== null) {
throw error
setPassword('');
setIsError(true);
}

if (session !== null) {
Expand All @@ -33,19 +33,68 @@ function LoginPage() {
}

return (
<div>
<VStack>
<div>
<label>Email: </label>
<input className='border' type="email" onChange={(e) => { setEmail(e.target.value) }} />
</div>
<div>
<label>Password:</label>
<input className='border' type="password" onChange={(e) => { setPassword(e.target.value) }} />
</div>
</VStack>
<button onClick={login}>Login</button>
</div>
<form
className='flex flex-col h-screen items-center justify-center px-5 gap-4 bg-gradient-to-b from-[#d9d9d9] to-[#6ccae8]'
onSubmit={login}
>
<input
type='text'
className='w-full h-14 rounded-full bg-white border border-black focus:border focus:border-black px-6 text-black font-bold text-xl placeholder:text-gray-300'
placeholder={Labels.account}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type='password'
className='w-full h-14 rounded-full bg-white border border-black focus:border focus:border-black px-6 text-black font-bold text-xl placeholder:text-gray-300'
placeholder={Labels.password}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<div className='flex flex-row items-center gap-4'>
{/* The style of the checkbox is refered from https://www.material-tailwind.com/docs/html/checkbox */}
<label htmlFor='remember' className='flex items-center cursor-pointer relative'>
<input
type='checkbox'
className='peer size-5 cursor-pointer transition-all appearance-none rounded bg-white border-2 border-gray-600 checked:bg-cyan-500 checked:border-cyan-500' id='remember'
checked={isRemember}
onChange={(e) => setIsRemember(e.target.checked)}
/>
<span className='absolute text-white opacity-0 peer-checked:opacity-100 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2'>
<svg xmlns='http://www.w3.org/2000/svg' className='h-3.5 w-3.5' viewBox='0 0 20 20' fill='currentColor' stroke='currentColor' strokeWidth='1' aria-hidden="true">
<path fillRule='evenodd' d='M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z' clipRule='evenodd'></path>
</svg>
</span>
</label>
<label htmlFor='remember' className='text-white font-bold text-2xl'>
{Labels.remember}
</label>
</div>
{
isError
? (
<span className='text-red-500 font-bold text-2xl'>
{isError ? Labels.wrongAccountOrPassword : ''}
</span>
)
: <></>
}
<button
type='submit'
className='w-full h-20 rounded-md bg-green-600 text-white font-extrabold text-5xl'
>
{Labels.login}
</button>
</form>
)
}

class Labels {
private constructor() { }

static readonly account = '信箱或手機號碼';
static readonly password = '密碼';
static readonly remember = '保持我的登入狀態';
static readonly login = '登入';
static readonly wrongAccountOrPassword = '帳號或密碼錯誤';
}
3 changes: 3 additions & 0 deletions src/utils/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type Database = {
end_time: string | null
fee: number | null
id: number
location: string | null
name: string | null
start_time: string | null
type: number | null
Expand All @@ -67,6 +68,7 @@ export type Database = {
end_time?: string | null
fee?: number | null
id?: number
location?: string | null
name?: string | null
start_time?: string | null
type?: number | null
Expand All @@ -78,6 +80,7 @@ export type Database = {
end_time?: string | null
fee?: number | null
id?: number
location?: string | null
name?: string | null
start_time?: string | null
type?: number | null
Expand Down
3 changes: 3 additions & 0 deletions supabase/migrations/20241114141253_remote_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter table "public"."events" add column "location" text;


0 comments on commit d5ebb7b

Please sign in to comment.