Skip to content

Commit

Permalink
Touch-up home page
Browse files Browse the repository at this point in the history
  • Loading branch information
myieye committed Jan 9, 2025
1 parent 1f6dc1f commit 4d3ada8
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 92 deletions.
170 changes: 92 additions & 78 deletions frontend/viewer/src/HomeView.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts">
<script lang="ts">
import {
mdiBookArrowDownOutline,
mdiBookArrowLeftOutline,
mdiBookEditOutline,
mdiBookPlusOutline,
mdiBookSyncOutline, mdiChatQuestion, mdiChevronRight, mdiCloud, mdiCloudSync,
mdiBookSyncOutline, mdiChatQuestion, mdiChevronRight, mdiCloud,
mdiTestTube,
} from '@mdi/js';
import {
Expand Down Expand Up @@ -34,7 +34,14 @@
const fwLiteConfig = useFwLiteConfig();
const exampleProjectName = 'Example-Project';
function dateTimeProjectSuffix(): string {
return new Date().toISOString()
.replace(/[^0-9]+/g, '-')
.replace(/-$/, '');
}
async function createProject(projectName: string) {
if ($isDev) projectName += `-dev-${dateTimeProjectSuffix()}`;
await projectsService.createProject(projectName);
await refreshProjects();
}
Expand Down Expand Up @@ -149,99 +156,102 @@
</AppBar>
<div class="contents md:flex md:flex-col md:w-3/4 lg:w-2/4 md:mx-auto md:py-4 md:min-h-full">
<div class="flex-grow hidden md:block"></div>
<div class="project-list md:border md:rounded md:p-4 md:pt-0">
<div class="project-list">
{#await projectsPromise}
<p>loading...</p>
{:then projects}
<div>
<p class="sub-title">Local</p>
<div class="space-y-4 md:space-y-8">
<div>
{#each projects.filter(p => p.crdt) as project (project.id)}
{@const server = syncedServer(remoteProjects, project, serversStatus)}
<AnchorListItem href={`/project/${project.name}`}>
<ListItem title={project.name}
icon={mdiBookEditOutline}
subheading={!server ? 'Local only' : ('Syncing with ' + server.displayName)}>
<div slot="actions">
<p class="sub-title">Local</p>
<div>
{#each projects.filter(p => p.crdt) as project, i (project.id ?? i)}
{@const server = syncedServer(remoteProjects, project, serversStatus)}
<AnchorListItem href={`/project/${project.name}`}>
<ListItem title={project.name}
icon={mdiBookEditOutline}
subheading={!server ? 'Local only' : ('Syncing with ' + server.displayName)}>
<div slot="actions" class="pointer-events-none">
<Button icon={mdiChevronRight} class="p-2"/>
</div>
</ListItem>
</AnchorListItem>
{/each}
<AnchorListItem href={`/testing/project-view`}>
<ListItem title="Test Project" icon={mdiTestTube}>
<div slot="actions" class="pointer-events-none">
<Button icon={mdiChevronRight} class="p-2"/>
</div>
</ListItem>
</AnchorListItem>
{/each}
<AnchorListItem href={`/testing/project-view`}>
<ListItem title="Test Project" icon={mdiTestTube}>
<div slot="actions">
<Button icon={mdiChevronRight} class="p-2"/>
</div>
</ListItem>
</AnchorListItem>
{#if !projects.some(p => p.name === exampleProjectName)}
<ListItem title="Create Example Project" on:click={() => createProject(exampleProjectName)}>
<div slot="actions">
<Button icon={mdiBookPlusOutline} class="p-2"/>
</div>
</ListItem>
{/if}
{#if !projects.some(p => p.name === exampleProjectName) || $isDev}
<ListItem title="Create Example Project" on:click={() => createProject(exampleProjectName)}>
<div slot="actions" class="pointer-events-none">
<Button icon={mdiBookPlusOutline} class="p-2"/>
</div>
</ListItem>
{/if}
</div>
</div>
{#each serversStatus as status}
{@const server = status.server}
{@const serverProjects = remoteProjects[server.authority]?.filter(p => p.crdt) ?? []}
<div class="flex flex-row items-center py-1 mr-2 mt-2">
<p class="pl-2 sub-title">{server.displayName} Server</p>
<div class="flex-grow"></div>
{#if status.loggedInAs}
<p class="mr-2 px-2 py-1 text-sm border rounded-full">{status.loggedInAs}</p>
{/if}
<LoginButton {server} isLoggedIn={status.loggedIn} on:status={() => refreshProjectsAndServers()}/>
</div>
<div>
{#if !serverProjects.length}
<p class="pl-2 text-surface-content/50 text-center">
{#if status.loggedIn}
No projects
{:else}
Login to see projects
{/if}
</p>
{/if}
{#each serverProjects as project}
{@const localProject = matchesProject(projects, project)}
<ListItem icon={mdiCloud}
class={localProject?.crdt ? 'pointer-events-none' : ''}
title={project.name}
on:click={() =>{ if (!localProject?.crdt) {void downloadCrdtProject(project, server);} }}
loading={downloading === project.name}>
<div slot="actions">
<Button disabled={localProject?.crdt} icon={localProject?.crdt ? mdiBookSyncOutline : mdiBookArrowDownOutline} class="p-2">
{localProject?.crdt ? 'Synced' : 'Download'}
</Button>
</div>
</ListItem>
{/each}
<div class="flex flex-row items-center mr-2 mb-2">
<p class="sub-title">{server.displayName} Server</p>
<div class="flex-grow"></div>
<LoginButton {status} on:status={() => refreshProjectsAndServers()}/>
</div>
<div>
{#if !serverProjects.length}
<p class="pl-2 text-surface-content/50 text-center">
{#if status.loggedIn}
No projects
{:else}
Login to see projects
{/if}
</p>
{/if}
{#each serverProjects as project}
{@const localProject = matchesProject(projects, project)}
<ListItem icon={mdiCloud}
class={localProject?.crdt ? 'pointer-events-none' : ''}
title={project.name}
on:click={() =>{ if (!localProject?.crdt) {void downloadCrdtProject(project, server);} }}
loading={downloading === project.name}>
<div slot="actions" class="pointer-events-none">
<Button disabled={localProject?.crdt} icon={localProject?.crdt ? mdiBookSyncOutline : mdiBookArrowDownOutline} class="p-2">
{localProject?.crdt ? 'Synced' : 'Download'}
</Button>
</div>
</ListItem>
{/each}
</div>
</div>
{/each}

{#if projects.some(p => p.fwdata)}
<p class="mt-4 mb-2 sub-title">Legacy FieldWorks Projects</p>
<div>
{#each projects.filter(p => p.fwdata) as project (project.id ?? project.name)}
<AnchorListItem href={`/fwdata/${project.name}`}>
<ListItem title={project.name}>
<img slot="avatar" src={flexLogo} alt="FieldWorks logo" class="h-6"/>
<div slot="actions">
<DevContent>
<Button
loading={importing === project.name}
icon={mdiBookArrowLeftOutline}
title="Import"
disabled={!!importing}
on:click={() => importFwDataProject(project.name)}>
</Button>
</DevContent>
</div>
</ListItem>
</AnchorListItem>
{/each}
<p class="sub-title">Legacy FieldWorks Projects</p>
<div>
{#each projects.filter(p => p.fwdata) as project (project.id ?? project.name)}
<AnchorListItem href={`/fwdata/${project.name}`}>
<ListItem title={project.name}>
<img slot="avatar" src={flexLogo} alt="FieldWorks logo" class="h-6"/>
<div slot="actions">
<DevContent invisible>
<Button
loading={importing === project.name}
icon={mdiBookArrowLeftOutline}
title="Import"
disabled={!!importing}
on:click={() => importFwDataProject(project.name)}>
</Button>
</DevContent>
</div>
</ListItem>
</AnchorListItem>
{/each}
</div>
</div>
{/if}
</div>
Expand All @@ -257,9 +267,13 @@
.project-list {
display: flex;
flex-direction: column;
:global(:is(.ListItem, .anchor-list-item)) {
@apply max-sm:!rounded-none;
}
}
.sub-title {
@apply pl-2 mt-5 mb-4;
@apply m-2;
@apply text-surface-content/50 text-sm;
}
</style>
27 changes: 14 additions & 13 deletions frontend/viewer/src/lib/auth/LoginButton.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script context="module" lang="ts">
import type {IAuthService} from '$lib/dotnet-types';
import type {IAuthService, IServerStatus} from '$lib/dotnet-types';
import {type Readable, writable, type Writable} from 'svelte/store';
let shouldUseSystemWebViewStore: Writable<boolean> | undefined = undefined;
Expand All @@ -13,8 +13,8 @@
</script>

<script lang="ts">
import {mdiLogin, mdiLogout} from '@mdi/js';
import {Button} from 'svelte-ux';
import {mdiAccountCircle, mdiLogin, mdiLogout} from '@mdi/js';
import {Button, Menu, MenuItem, Toggle} from 'svelte-ux';
import type {ILexboxServer} from '$lib/dotnet-types';
import {useAuthService} from '$lib/services/service-provider';
import {createEventDispatcher} from 'svelte';
Expand All @@ -24,8 +24,8 @@
const dispatch = createEventDispatcher<{
status: 'logged-in' | 'logged-out'
}>();
export let isLoggedIn: boolean;
export let server: ILexboxServer;
export let status: IServerStatus;
$: server = status.server;
let loading = false;
Expand All @@ -50,14 +50,15 @@
}
</script>

{#if isLoggedIn}
<Button {loading}
variant="fill"
color="primary"
on:click={() => logout(server)}
icon={mdiLogout}>
Logout
</Button>
{#if status.loggedIn}
<Toggle let:on={open} let:toggle let:toggleOff>
<Button on:click={toggle} {loading} variant="fill" color="primary" icon={mdiAccountCircle}>
{status.loggedInAs}
<Menu {open} on:close={toggleOff} placement="bottom-end">
<MenuItem icon={mdiLogout} on:click={() => logout(server)}>Logout</MenuItem>
</Menu>
</Button>
</Toggle>
{:else}
{#if $shouldUseSystemWebView}
<Button {loading}
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/lib/entry-editor/DeleteDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</script>
<Dialog {open} on:close={cancel} {loading} persistent={loading} style="height: auto">
<div slot="title">Delete {subject}</div>
<div class="m-6">
<div class="m-6 mt-3">
<p>Are you sure you want to delete {subject}?</p>
</div>
<div slot="actions">
Expand Down
8 changes: 8 additions & 0 deletions frontend/viewer/src/lib/layout/DevContent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
isDev.set(localStorage.getItem('devMode') === 'true');
</script>

<script lang="ts">
export let invisible = false;
</script>

{#if $isDev}
<slot />
{:else if invisible}
<div class="invisible">
<slot />
</div>
{/if}

0 comments on commit 4d3ada8

Please sign in to comment.