Skip to content

Commit

Permalink
Merge branch 'main' into linguistic-history
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob-8 authored Jan 30, 2025
2 parents 362b5bd + d7a112c commit 35b207f
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 132 deletions.
28 changes: 12 additions & 16 deletions packages/site/src/docs/Supabase.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
# Local [Supabase](https://supabase.com/docs) dev

Schema:
https://drawsql.app/teams/ld-4/diagrams/entries-sentences-texts
https://supabase.com/dashboard/project/actkqboqpzniojhgtqzw/database/schemas
http://127.0.0.1:54323/project/default/database/schemas

Handle speakers id being uuid and lots of connections with FB speaker ids not matching, 20240222001122_media-tables.sql

## Setup

1. [Install supabase cli locally](https://supabase.com/docs/guides/cli) *- you can skip this the first few times and just prepend `pnpx ` to the commands below, but after awhile you will tire of waiting for pnpx on each command*
2. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) and make sure it is running

## Development

- `supabase start` to start a local instance of supabase, including POSTGres, Storage, Email, Functions, etc...
- `supabase status` at any later point to get local urls if you've lost them in terminal history
- `supabase migration new <feature-name>` to write a new sql migration that will edit the database. Changes are saved in version control under supabase/migrations.
- `supabase migration up` to apply pending migrations to local database (prefer running `supabase db reset` instead)
- `supabase db reset` to wipe the database, run each migration sequentially and then finally the `seed.sql` file.
Expand All @@ -29,23 +24,24 @@ Local:
Deployed (we don't use this):
- `supabase gen types typescript --project-id=actkqboqpzniojhgtqzw --schema public > packages/site/src/lib/supabase/generated.types.ts`

## Tests

See [pgTAP docs](https://pgtap.org/documentation.html) and https://supabase.com/docs/guides/database/extensions/pgtap

- `supabase test new <name>` to create a new test file
- `supabase test db` to run tests

## Push config changes and new migrations to cloud project
You can check current prod migrations at https://supabase.com/dashboard/project/actkqboqpzniojhgtqzw/database/migrations

- `supabase login`
- `supabase link --project-ref=actkqboqpzniojhgtqzw --password=<DB password>`
- `supabase db push`

## Misc
## Schema
https://drawsql.app/teams/ld-4/diagrams/entries-sentences-texts
https://supabase.com/dashboard/project/actkqboqpzniojhgtqzw/database/schemas
http://127.0.0.1:54323/project/default/database/schemas

- `supabase status` check status and get local urls
## Tests

See [pgTAP docs](https://pgtap.org/documentation.html) and https://supabase.com/docs/guides/database/extensions/pgtap

- `supabase test new <name>` to create a new test file
- `supabase test db` to run tests

## Use data from a `pg_dump` backup locally

Expand Down
2 changes: 2 additions & 0 deletions packages/site/src/routes/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const load: PageLoad = ({ parent }) => {
const { data: private_dictionaries, error } = await supabase.from('materialized_dictionaries_view')
.select()
.neq('public', true)
.is('con_language_description', null)

if (error) {
console.error(error)
}
Expand Down
16 changes: 8 additions & 8 deletions packages/site/src/routes/admin/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<script lang="ts">
import SeoMetaTags from '$lib/components/SeoMetaTags.svelte';
import Header from '$lib/components/shell/Header.svelte';
import AdminGuard from '$lib/components/ui/AdminGuard.svelte';
import Tab from './Tab.svelte';
const tabs = ['users', 'dictionaries'];
import Tab from './Tab.svelte'
import SeoMetaTags from '$lib/components/SeoMetaTags.svelte'
import Header from '$lib/components/shell/Header.svelte'
import AdminGuard from '$lib/components/ui/AdminGuard.svelte'
</script>

<SeoMetaTags title="Admin Panel" />
Expand All @@ -13,9 +12,10 @@
<AdminGuard>
<div class="px-3 border-b border-gray-200">
<nav>
{#each tabs as tab}
<Tab link={tab} />
{/each}
<Tab link="users" label="users" />
<Tab link="dictionaries?filter=public" label="public dictionaries" />
<Tab link="dictionaries?filter=private" label="private dictionaries" />
<Tab link="dictionaries?filter=other" label="other dictionaries" />
</nav>
</div>

Expand Down
10 changes: 6 additions & 4 deletions packages/site/src/routes/admin/Tab.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script lang="ts">
export let link = '';
import { page } from '$app/stores';
import { page } from '$app/stores'
export let link: string
export let label: string
</script>

<a
href={`/admin/${link}`}
class={$page.url.pathname.match(/(\w+)$/)[0] === link ? 'active' : 'inactive'}>
{link}
class={$page.url.href.split('/').pop() === link ? 'active' : 'inactive'}>
{label}
</a>

<style>
Expand Down
81 changes: 21 additions & 60 deletions packages/site/src/routes/admin/dictionaries/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<script lang="ts">
import type { IHelper, IInvite, TablesUpdate } from '@living-dictionaries/types'
import type { DictionaryView, IHelper, IInvite, TablesUpdate } from '@living-dictionaries/types'
import { collectionStore, getCollection } from 'sveltefirets'
import { Button, IntersectionObserverShared, ResponsiveTable } from 'svelte-pieces'
import { where } from 'firebase/firestore'
import { onMount } from 'svelte'
import DictionaryRow from './DictionaryRow.svelte'
import SortDictionaries from './SortDictionaries.svelte'
import type { DictionaryWithHelperStores } from './dictionaryWithHelpers'
import { exportAdminDictionariesAsCSV } from './export'
import type { PageData } from './$types'
import Filter from '$lib/components/Filter.svelte'
import { api_update_dictionary } from '$api/db/update-dictionary/_call'
import { browser } from '$app/environment'
import { page } from '$app/stores'
export let data: PageData
Expand All @@ -19,18 +20,24 @@
let dictionariesAndHelpers: DictionaryWithHelperStores[] = []
onMount(async () => {
dictionariesAndHelpers = await get_dictionaries_with_helpers()
})
$: active_section = $page.url.searchParams.get('filter') as 'public' | 'private' | 'other'
async function get_dictionaries_with_helpers() {
const { data: dictionaries, error } = await data.supabase.from('dictionaries_view')
.select()
if (error) {
console.error(error)
alert(error.message)
return []
$: if (browser) {
get_dictionaries_with_helpers(active_section).then(dictionaries => dictionariesAndHelpers = dictionaries)
}
async function get_dictionaries_with_helpers(section: 'public' | 'private' | 'other') {
let dictionaries: DictionaryView[]
if (section === 'public') {
dictionaries = await data.get_public_dictionaries()
} else if (section === 'private') {
dictionaries = await data.get_private_dictionaries()
} else if (section === 'other') {
dictionaries = await data.get_other_dictionaries()
} else {
dictionaries = await data.get_public_dictionaries()
}
return dictionaries.map((dictionary) => {
return {
...dictionary,
Expand All @@ -49,7 +56,7 @@
async function update_dictionary(change: TablesUpdate<'dictionaries'> & { id: string }) {
try {
await api_update_dictionary(change)
dictionariesAndHelpers = await get_dictionaries_with_helpers()
dictionariesAndHelpers = await get_dictionaries_with_helpers(active_section)
} catch (err) {
alert(`Error: ${err}`)
}
Expand Down Expand Up @@ -82,53 +89,7 @@
<IntersectionObserverShared bottom={2000} let:intersecting once>
<tr>
{#if intersecting}
<DictionaryRow
{index}
{dictionary}
on:toggleprivacy={() => {
update_dictionary({
id: dictionary.id,
public: !dictionary.public,
})
}}
on:addalternatename={(event) => {
update_dictionary({
id: dictionary.id,
alternate_names: [...(dictionary.alternate_names || []), event.detail],
})
}}
on:removealternatename={(event) => {
update_dictionary({
id: dictionary.id,
alternate_names: dictionary.alternate_names.filter(name => name !== event.detail),
})
}}
on:updatecoordinates={({ detail: { lat, lng } }) => {
const [, ...rest] = dictionary.coordinates?.points || []
update_dictionary({
id: dictionary.id,
coordinates: {
points: [{ coordinates: { latitude: lat, longitude: lng } }, ...rest],
regions: dictionary.coordinates?.regions,
},
})
}}
on:removecoordinates={() => {
const [, ...rest] = dictionary.coordinates?.points || []
update_dictionary({
id: dictionary.id,
coordinates: {
points: rest,
regions: dictionary.coordinates?.regions,
},
})
}}
on:toggleconlang={() => {
update_dictionary({
id: dictionary.id,
con_language_description: !dictionary.con_language_description ? 'YES' : null,
})
}} />
<DictionaryRow {index} {dictionary} {update_dictionary} />
{:else}
<td colspan="30"> Loading... </td>
{/if}
Expand Down
49 changes: 49 additions & 0 deletions packages/site/src/routes/admin/dictionaries/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { DictionaryView } from '@living-dictionaries/types'
import type { PageLoad } from './$types'

export const load: PageLoad = ({ parent }) => {
async function get_public_dictionaries() {
const { supabase } = await parent()
const { data: public_dictionaries, error } = await supabase.from('dictionaries_view')
.select()
.eq('public', true)
if (error) {
console.error(error)
alert(error.message)
return []
}
return public_dictionaries as DictionaryView[]
}

async function get_private_dictionaries() {
const { supabase } = await parent()
const { data: private_dictionaries, error } = await supabase.from('dictionaries_view')
.select()
.neq('public', true)
.is('con_language_description', null)

if (error) {
console.error(error)
alert(error.message)
return []
}
return private_dictionaries as DictionaryView[]
}

async function get_other_dictionaries() {
const { supabase } = await parent()
const { data: private_dictionaries, error } = await supabase.from('dictionaries_view')
.select()
.neq('public', true)
.not('con_language_description', 'is', null)

if (error) {
console.error(error)
alert(error.message)
return []
}
return private_dictionaries as DictionaryView[]
}

return { get_public_dictionaries, get_private_dictionaries, get_other_dictionaries }
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
<script lang="ts">
import { updateOnline } from 'sveltefirets';
export let field;
export let value;
export let dictionaryId;
import type { TablesUpdate } from '@living-dictionaries/types'
export let field: keyof TablesUpdate<'dictionaries'>
export let value: string
export let dictionary_id: string
export let update_dictionary: (change: TablesUpdate<'dictionaries'> & { id: string }) => Promise<void>
let debouncedSaveTimer: NodeJS.Timeout
let unsaved = false
let debouncedSaveTimer;
let unsaved = false;
function valueChanged() {
unsaved = true;
clearTimeout(debouncedSaveTimer);
debouncedSaveTimer = setTimeout(save, 2000);
unsaved = true
clearTimeout(debouncedSaveTimer)
debouncedSaveTimer = setTimeout(save, 2000)
}
async function save() {
try {
await updateOnline(`dictionaries/${dictionaryId}`, { [field]: value });
unsaved = false;
await update_dictionary({ id: dictionary_id, [field]: value })
unsaved = false
} catch (err) {
alert(err);
alert(err)
}
// const data = await db.collection('dictionaries').doc(dictionaryId).get();
// if (data.exists) {
// console.log(data.data());
// } else {
// this.error("no data");
// }
}
</script>

Expand Down
Loading

0 comments on commit 35b207f

Please sign in to comment.