Skip to content

Commit

Permalink
Display related projects in project create page
Browse files Browse the repository at this point in the history
If the org dropdown has a selected org, and there is a valid language
code (2 or 3 characters), then a search will be made for any projects
belonging to the selected org that have that language code in their list
of currently active vernacular writing systems.
  • Loading branch information
rmunn committed Jul 24, 2024
1 parent 9144205 commit 29e0632
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
27 changes: 27 additions & 0 deletions frontend/src/lib/util/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,30 @@ export function deriveAsync<T, D>(
}, debounceTime);
}, initialValue);
}

/**
* @param fn A function that maps the store value to an async result, filtering out undefined values
* @returns A store that contains the result of the async function
*/
export function deriveAsyncIfDefined<T, D>(
store: Readable<T | undefined>,
fn: (value: T) => Promise<D>,
initialValue?: D,
debounce: number | boolean = false): Readable<D> {

const debounceTime = pickDebounceTime(debounce);
let timeout: ReturnType<typeof setTimeout> | undefined;

return derived(store, (value, set) => {
if (value) {
clearTimeout(timeout);
timeout = setTimeout(() => {
const myTimeout = timeout;
void fn(value).then((result) => {
if (myTimeout !== timeout) return; // discard outdated results
set(result);
});
}, debounceTime);
}
}, initialValue);
}
28 changes: 27 additions & 1 deletion frontend/src/routes/(authenticated)/project/create/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { _createProject, _projectCodeAvailable } from './+page';
import AdminContent from '$lib/layout/AdminContent.svelte';
import { useNotifications } from '$lib/notify';
import { Duration, deriveAsync } from '$lib/util/time';
import { Duration, deriveAsync, deriveAsyncIfDefined } from '$lib/util/time';
import { getSearchParamValues } from '$lib/util/query-params';
import { onMount } from 'svelte';
import MemberBadge from '$lib/components/Badges/MemberBadge.svelte';
Expand All @@ -18,6 +18,7 @@
import { ProjectConfidentialityCombobox } from '$lib/components/Projects';
import DevContent from '$lib/layout/DevContent.svelte';
import { isDev } from '$lib/layout/DevContent.svelte';
import { _getProjectsByLangCodeAndOrg } from './+page';
export let data;
$: user = data.user;
Expand Down Expand Up @@ -85,6 +86,19 @@
$: $asyncCodeError = $codeIsAvailable ? undefined : $t('project.create.code_exists');
const codeErrors = derived([errors, asyncCodeError], () => [...new Set(concatAll($errors.code, $asyncCodeError))]);
const langCodeStore = derived(form, ($form) => $form.languageCode);
const orgIdStore = derived(form, ($form) => $form.orgId);
const langCodeAndOrgIdStore = derived([langCodeStore, orgIdStore], ([lang, orgId], set) => {
if (lang && orgId && (lang.length == 2 || lang.length == 3)) {
set({ langCode: lang, orgId: orgId });
}
});
const relatedProjectsStoreStore = deriveAsyncIfDefined(langCodeAndOrgIdStore, _getProjectsByLangCodeAndOrg);
const relatedProjects = derived(relatedProjectsStoreStore, (nestedStore, set) => {
if (nestedStore) return nestedStore.subscribe(set); // Return the unsubscribe fn so we don't leak memory
}, []);
const typeCodeMap: Partial<Record<ProjectType, string | undefined>> = {
[ProjectType.FlEx]: 'flex',
[ProjectType.WeSay]: 'dictionary',
Expand Down Expand Up @@ -212,6 +226,18 @@
bind:value={$form.languageCode}
error={$errors.languageCode}
/>

{#if $relatedProjects?.length}
<div>
Possibly related projects:
<ul>
{#each $relatedProjects as proj}
<li>{proj.name} ({proj.code})</li>
{/each}
</ul>
</div>
{/if}

<AdminContent>
<Checkbox label={$t('project.create.custom_code')} bind:value={$form.customCode} />
</AdminContent>
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/routes/(authenticated)/project/create/+page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { $OpResult, CreateProjectInput, CreateProjectMutation } from '$lib/gql/types';
import type { $OpResult, CreateProjectInput, CreateProjectMutation, ProjectsByLangCodeAndOrgQuery } from '$lib/gql/types';
import { getClient, graphql } from '$lib/gql';

import type { PageLoadEvent } from './$types';
import { getSearchParam } from '$lib/util/query-params';
import { isGuid } from '$lib/util/guid';
import type { Readable } from 'svelte/store';

export async function load(event: PageLoadEvent) {
const userIsAdmin = (await event.parent()).user.isAdmin;
Expand Down Expand Up @@ -88,3 +89,20 @@ export async function _projectCodeAvailable(code: string): Promise<boolean> {
if (!result.ok) throw new Error('Failed to check project code availability');
return await result.json() as boolean;
}

export async function _getProjectsByLangCodeAndOrg(input: { orgId: string, langCode: string }): Promise<Readable<ProjectsByLangCodeAndOrgQuery['projectsByLangCodeAndOrg']>> {
const client = getClient();
//language=GraphQL
const results = await client.awaitedQueryStore(fetch,
graphql(`
query ProjectsByLangCodeAndOrg($input: ProjectsByLangCodeAndOrgInput!) {
projectsByLangCodeAndOrg(input: $input) {
id
code
name
}
}
`), { input }
);
return results.projectsByLangCodeAndOrg;
}

0 comments on commit 29e0632

Please sign in to comment.