-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Generic data model and Supabase data service
A generic data template model has been implemented. This model can be extended to other classes, such as User. It includes the TABLE_NAME and DATABASE_MAPPING properties for a more efficient database access. The SupabaseService has been implemented to support general data fetching. It currently supports the fetching of a single object and an array of objects. There is no need to implement the data fetching logic repeatedly for each model. - Added SupabaseService - Added GenericModel - Added User model - Added data fetching tests during Homepage initialization
- Loading branch information
1 parent
7134cae
commit d3b748f
Showing
4 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// GenericModel class | ||
export default abstract class GenericModel { | ||
// Abstract property for table name | ||
static TABLE_NAME: string; | ||
|
||
static DATABASE_MAP: Object; | ||
|
||
// Abstract method for parsing JSON | ||
static parseJson(json: any): GenericModel { | ||
throw new Error("parseJson method not implemented."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import GenericModel from "./GenericModel" | ||
|
||
export default class User extends GenericModel { | ||
|
||
static TABLE_NAME = "members" | ||
static DATABASE_MAP: Object = { | ||
id: "uuid", | ||
username: "username", | ||
email: "fk_email", | ||
identity: "fk_identity", | ||
avatar: "avatar", | ||
} | ||
|
||
public id: String = "" | ||
public username: String = "" | ||
public email: String = "" | ||
public phone: String = "" | ||
public avatar: String = "" | ||
public profileBackground: String = "" | ||
public joinedAt: Date = new Date() | ||
public identity: String = "" | ||
public department: String = "" | ||
public grade: String = "" | ||
public bio: String = "" | ||
|
||
|
||
static parseJson(json: any) : User { | ||
const user = new User() | ||
|
||
for (const [userProperty, jsonField] of Object.entries(this.DATABASE_MAP)) | ||
if (json[jsonField] !== undefined) | ||
(user as any)[userProperty] = json[jsonField]; | ||
|
||
return user | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { createFileRoute } from '@tanstack/react-router' | ||
import { VStack } from '../../components' | ||
|
||
const homeItems = [ | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
{ title: "test", description: "Hello world." }, | ||
] | ||
|
||
// User.fetchSingle().then(user => { | ||
// console.log(user) | ||
// }) | ||
|
||
// const supabaseService = new SupabaseService() | ||
// SupabaseService.fetchSingle(User, "c7e80cb0-7d3d-411f-9983-5e3addf62980", { email: "ncuapp@test.com" }).then( | ||
// response => { | ||
// console.log(response) | ||
// } | ||
// ) | ||
|
||
// SupabaseService.fetchMultiple(User).then( | ||
// response => { | ||
// console.log(response) | ||
// } | ||
// ) | ||
|
||
// async function fetch() { | ||
// const { data } = await supabase.from('members').select('*') | ||
// console.log(data) | ||
// } | ||
// fetch() | ||
|
||
export const Route = createFileRoute('/home/')({ | ||
|
||
component: () => <div> | ||
<section className='h-[200px] mb-[40px] bg-yellow-50 relative border-b-[5px] border-gray-600'> | ||
|
||
<div className='flex flex-col absolute left-[10px] bottom-[5px]'> | ||
<h1 className='font-bold text-4xl text-black'>這裡最多七個字</h1> | ||
<h2 className='font-bold text-black'>中文系 二年級</h2> | ||
</div> | ||
|
||
{/* <div className=' | ||
w-[125px] h-[125px] | ||
absolute right-[15px] bottom-[-25px] | ||
border-[4px] border-gray-600 rounded-full bg-gray-500 | ||
'></div> */} | ||
|
||
<div className="avatar absolute right-[15px] bottom-[-25px]"> | ||
<div className="ring-gray-300 w-[125px] rounded-full ring"> | ||
<img src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" /> | ||
</div> | ||
</div> | ||
|
||
<span className=' | ||
absolute top-[20px] right-[15px] | ||
text-black font-bold text-xs | ||
'> | ||
變更個人檔案 | ||
</span> | ||
</section> | ||
|
||
<VStack className='px-[10px] gap-[20px]'> | ||
|
||
|
||
{/* {homeItems.map((item, index) => ( | ||
<WelcomeCard title={item.title} description={item.description} /> | ||
))} */} | ||
|
||
{/* <NcuInput /> | ||
<NcuTextarea> | ||
Hello | ||
</NcuTextarea> | ||
<NcuSwitch label="test">X</NcuSwitch> | ||
<NcuButton>Test</NcuButton> */} | ||
</VStack> | ||
</div>, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import GenericModel from "../models/GenericModel"; | ||
import { supabase } from "../utils/supabase"; | ||
|
||
// Supabase service for quickly accessing data | ||
export default class SupabaseService { | ||
|
||
|
||
/* Fetch a single object | ||
* | ||
* Parameters | ||
* ---------- | ||
* modelClass: class | ||
* The target model class | ||
* targetID: str | ||
* The target record ID | ||
* criteria: Dictionary | ||
* Extra search criteria, please follow the DATABASE_MAPPING of the target model | ||
* | ||
* Returns | ||
* ------- | ||
* The target object (of the target model type). Null if record is not found. | ||
*/ | ||
public static async fetchSingle<T extends GenericModel>( | ||
modelClass: new() => T, | ||
targetID: String, | ||
criteria?: Partial<Record<string, any>>, | ||
): Promise<T | null> { | ||
const model = (modelClass as any) | ||
const tableName = model.TABLE_NAME | ||
|
||
var query = supabase.from(tableName) | ||
.select('*') | ||
.eq("uuid", targetID) | ||
|
||
for (const key in criteria) | ||
if (model.DATABASE_MAP[key]) | ||
query = query.eq(model.DATABASE_MAP[key], criteria[key]) | ||
|
||
const data = await query; | ||
|
||
if (!data.data || data.data.length == 0) { | ||
return null | ||
} | ||
const record = data.data[0] | ||
|
||
return model.parseJson(record) | ||
} | ||
|
||
|
||
|
||
|
||
/* Fetch an array of objects | ||
* | ||
* Parameters | ||
* ---------- | ||
* modelClass: class | ||
* The target model class | ||
* | ||
* Returns | ||
* ------- | ||
* Array of objects (of the target model type). | ||
* Returns empty array if no records can be found. | ||
*/ | ||
public static async fetchMultiple<T extends GenericModel>( | ||
modelClass: new() => T, | ||
): Promise<Array<T>> { | ||
const model = (modelClass as any) | ||
const tableName = model.TABLE_NAME | ||
|
||
const data = await supabase.from(tableName) | ||
.select('*') | ||
|
||
const records = data.data | ||
|
||
const results: Array<T> = [] | ||
|
||
records?.forEach(record => { | ||
const user = model.parseJson(record) | ||
results.push(user) | ||
}) | ||
|
||
return results | ||
} | ||
} |