-
Notifications
You must be signed in to change notification settings - Fork 191
Feat: multi-terminology support #2413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 8.x
Are you sure you want to change the base?
Changes from 18 commits
f6f2d94
3fae9d6
bc16dea
cd5c781
e451f18
a3eb1c8
5eca86e
aca5241
88a153e
d00e280
7410780
8f5f4c1
645ae62
6cecb2c
4354c90
e87894d
ef7d6d4
d2c2870
d5afa9b
466cc8d
31aa0ff
e1aa598
74e6eae
e06980a
95bbe56
4288215
a871810
470a4e1
7d7dfab
fc78c44
6fef9a2
4ef68a4
2ce3811
db8420d
aea5e82
2abb2bc
9d1f4ce
ea665d7
8a449d5
43eff98
e97ef6c
f0d1a5d
f49f888
1f254a6
604f0ab
d53c8ef
ae9aa07
0e7938a
222eb9a
7c4c256
83a8870
f1712be
7a4f9a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { resolve } from '$app/paths'; | ||
import { goto } from '$app/navigation'; | ||
import type { Pathname, RouteId, RouteParams } from '$app/types'; | ||
|
||
// taken directly from svelte's source! | ||
type ResolveArgs<T extends RouteId | Pathname> = T extends RouteId | ||
? RouteParams<T> extends Record<string, never> | ||
? [route: T] | ||
: [route: T, params: RouteParams<T>] | ||
: [route: T]; | ||
|
||
export function withPath(base: string, ...parts: string[]) { | ||
return [base.replace(/\/+$/, ''), ...parts].join('/'); | ||
} | ||
|
||
export function resolveRoute<T extends RouteId>(route: T, params?: Record<string, string>) { | ||
// type cast is necessary here! | ||
const resolveArgs = params ? ([route, params] as [T, RouteParams<T>]) : [route]; | ||
|
||
return resolve(...(resolveArgs as ResolveArgs<T>)); | ||
} | ||
ItzNotABug marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
export function navigate<T extends RouteId>( | ||
route: T, | ||
params?: Record<string, string> | ||
): Promise<void> { | ||
// type cast is necessary here! | ||
return goto(resolveRoute(route, params)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import type { Page } from '@sveltejs/kit'; | ||
import { page as pageState } from '$app/state'; | ||
|
||
import { useTerminology } from './terminology'; | ||
import { Submit, Click } from '$lib/actions/analytics'; | ||
import type { AnalyticsResult, TerminologyResult, TerminologyShape } from './types'; | ||
|
||
export function useAnalytics(pageOrTerms: Page | TerminologyResult = pageState): AnalyticsResult { | ||
const terminology = 'source' in pageOrTerms ? pageOrTerms : useTerminology(pageOrTerms); | ||
|
||
const createSubmitHandler = <TAction extends string>(termType: keyof TerminologyShape) => { | ||
return (action: TAction) => { | ||
const term = terminology.source[termType]; | ||
if (!term) { | ||
throw new Error(`No ${termType} terminology found`); | ||
} | ||
const enumKey = `${term.title.singular}${action}`; | ||
return Submit[enumKey as keyof typeof Submit]; | ||
abnegate marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
}; | ||
|
||
const createClickHandler = <TAction extends string>(termType: keyof TerminologyShape) => { | ||
return (action: TAction) => { | ||
const term = terminology.source[termType]; | ||
if (!term) { | ||
throw new Error(`No ${termType} terminology found`); | ||
} | ||
const enumKey = `Database${term.title.singular}${action}`; | ||
return Click[enumKey as keyof typeof Click]; | ||
abnegate marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
}; | ||
|
||
const result: AnalyticsResult = { submit: {}, click: {} }; | ||
|
||
if (terminology.entity) { | ||
result.click.entity = createClickHandler('entity'); | ||
result.submit.entity = createSubmitHandler('entity'); | ||
} | ||
|
||
if (terminology.field) { | ||
result.click.field = createClickHandler('field'); | ||
result.submit.field = createSubmitHandler('field'); | ||
} | ||
|
||
if (terminology.record) { | ||
result.click.record = createClickHandler('record'); | ||
result.submit.record = createSubmitHandler('record'); | ||
} | ||
|
||
return result; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import type { Page } from '@sveltejs/kit'; | ||
import { page as pageState } from '$app/state'; | ||
|
||
import { useTerminology } from './terminology'; | ||
import { Dependencies } from '$lib/constants'; | ||
import type { DependenciesResult, Term, TerminologyResult } from './types'; | ||
|
||
export function useDependencies( | ||
pageOrTerms: Page | TerminologyResult = pageState | ||
): DependenciesResult { | ||
// source is in `TerminologyResult`. | ||
const terminology = 'source' in pageOrTerms ? pageOrTerms : useTerminology(pageOrTerms); | ||
|
||
const getDependencies = (term: { title: Term }) => ({ | ||
singular: Dependencies[term.title.singular.toUpperCase() as keyof typeof Dependencies], | ||
plural: Dependencies[term.title.plural.toUpperCase() as keyof typeof Dependencies] | ||
}); | ||
|
||
return { | ||
entity: terminology.entity ? getDependencies(terminology.entity) : undefined, | ||
field: terminology.field ? getDependencies(terminology.field) : undefined, | ||
record: terminology.record ? getDependencies(terminology.record) : undefined | ||
}; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be in a "helpers" context? Can't it just be defined for all entities There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would just mix up the logic with stranded TS files while the views has its own directory structure. Mainly for organizing TS ops in a specific directory so its easier to manage. Maybe |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from './init'; | ||
export * from './types'; | ||
export * from './analytics'; | ||
export * from './terminology'; | ||
export * from './dependencies'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import type { Page } from '@sveltejs/kit'; | ||
import { getContext, setContext } from 'svelte'; | ||
import { | ||
type AnalyticsResult, | ||
type DependenciesResult, | ||
type TerminologyResult, | ||
useAnalytics, | ||
useDependencies, | ||
useTerminology | ||
} from '$database/(entity)'; | ||
|
||
const TERMINOLOGIES_KEY = Symbol('terminologies'); | ||
|
||
export type Terminologies = { | ||
analytics: AnalyticsResult; | ||
terminology: TerminologyResult; | ||
dependencies: DependenciesResult; | ||
}; | ||
|
||
export function getTerminologies(): Terminologies { | ||
return getContext<Terminologies>(TERMINOLOGIES_KEY); | ||
} | ||
|
||
export function setTerminologies(page: Page) { | ||
setContext(TERMINOLOGIES_KEY, buildTerminologies(page)); | ||
} | ||
|
||
function buildTerminologies(page: Page) { | ||
const terminology = useTerminology(page); | ||
return { | ||
terminology, | ||
analytics: useAnalytics(terminology), | ||
dependencies: useDependencies(terminology) | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import type { Page } from '@sveltejs/kit'; | ||
import { page as pageStage } from '$app/state'; | ||
|
||
import { type Models } from '@appwrite.io/console'; | ||
import { capitalize, plural } from '$lib/helpers/string'; | ||
import type { Columns } from '$database/table-[table]/store'; | ||
import type { Term, TerminologyResult, TerminologyShape } from '$database/(entity)/helpers/types'; | ||
|
||
export type DatabaseType = 'legacy' | 'tablesdb' | 'documentsdb' | 'vectordb'; | ||
export type Entity = Partial<Models.Table>; | ||
export type Field = Partial<Columns>; | ||
export type Index = Partial<Models.Index | Models.ColumnIndex>; | ||
|
||
export const baseTerminology = { | ||
tablesdb: { | ||
entity: 'table', | ||
field: 'column', | ||
record: 'row' | ||
}, | ||
documentsdb: { | ||
entity: 'collection', | ||
record: 'document' | ||
ItzNotABug marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
legacy: { | ||
entity: 'collection', | ||
field: 'attribute', | ||
record: 'document' | ||
}, | ||
vectordb: {} | ||
abnegate marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} as const; | ||
|
||
const createTerm = (singular: string, pluralForm: string): Term => { | ||
return { singular, plural: pluralForm }; | ||
}; | ||
|
||
// transforms a base into lower/title variants | ||
const createTermVariants = (baseTerm: string) => ({ | ||
lower: createTerm(baseTerm, plural(baseTerm)), | ||
title: createTerm(capitalize(baseTerm), plural(capitalize(baseTerm))) | ||
}); | ||
|
||
// transforms terminology for a database type | ||
const transformDatabaseTerms = (terms: Partial<TerminologyShape>) => | ||
Object.fromEntries( | ||
Object.entries(terms).map(([key, term]) => [ | ||
key, | ||
term ? createTermVariants(term) : undefined | ||
]) | ||
); | ||
|
||
// build the terminology data | ||
const terminologyData = Object.fromEntries( | ||
Object.entries(baseTerminology).map(([dbType, terms]) => [ | ||
dbType, | ||
transformDatabaseTerms(terms) | ||
]) | ||
); | ||
|
||
export function useTerminology(page: Page = pageStage): TerminologyResult { | ||
const type = page.data?.database?.type as DatabaseType; | ||
const dbTerminologies = terminologyData[type] || {}; | ||
return { | ||
source: dbTerminologies, | ||
field: dbTerminologies.field, | ||
record: dbTerminologies.record, | ||
entity: dbTerminologies.entity | ||
}; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.