Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
publicGetOnlineDevicesStatistics,
publicGetPublicShare,
sessionsDeleteSession,
sessionsGetSelfSession,
sessionsListSessions,
shareLinksAddShocker,
shareLinksCreatePublicShare,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/Table/DataTableTemplate.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
});
</script>

<div class={cn('overflow-y-auto rounded-md border', className)}>
<div class={cn('overflow-x-auto rounded-md border', className)}>
<Table.Root>
<Table.Header>
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(app)/home/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</div>

<!-- Stats cards -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3">
<Card.Root>
<Card.Header class="flex flex-row items-center justify-between pb-2">
<Card.Title class="text-sm font-medium">Shockers</Card.Title>
Expand Down Expand Up @@ -83,7 +83,7 @@
<!-- Quick links -->
<div>
<h2 class="mb-3 text-lg font-semibold">Quick Links</h2>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
<div class="grid grid-cols-2 gap-3 lg:grid-cols-4">
<Button
variant="outline"
class="h-auto justify-start gap-3 p-4"
Expand Down
62 changes: 32 additions & 30 deletions src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -356,38 +356,40 @@
No update history found for this hub.
</p>
{:else}
<Table.Root class="w-full">
<Table.Header>
<Table.Row>
<Table.Head>ID</Table.Head>
<Table.Head>Started</Table.Head>
<Table.Head>Status</Table.Head>
<Table.Head>Version</Table.Head>
<Table.Head>Message</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each otaLogs as otaLog (otaLog.id)}
<div class="overflow-x-auto">
<Table.Root class="w-full">
<Table.Header>
<Table.Row>
<Table.Cell class="text-muted-foreground font-mono text-xs">
{NumberToHexPadded(otaLog.id, 8)}
</Table.Cell>
<Table.Cell class="text-sm" title={otaLog.startedAt.toLocaleString()}>
{formatRelativeTime(otaLog.startedAt)}
</Table.Cell>
<Table.Cell>
<Badge variant={getStatusBadgeVariant(otaLog.status)}>
{otaLog.status}
</Badge>
</Table.Cell>
<Table.Cell class="font-mono text-sm">{otaLog.version}</Table.Cell>
<Table.Cell class="text-muted-foreground max-w-xs truncate text-sm">
{otaLog.message ?? '-'}
</Table.Cell>
<Table.Head>ID</Table.Head>
<Table.Head>Started</Table.Head>
<Table.Head>Status</Table.Head>
<Table.Head>Version</Table.Head>
<Table.Head>Message</Table.Head>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</Table.Header>
<Table.Body>
{#each otaLogs as otaLog (otaLog.id)}
<Table.Row>
<Table.Cell class="text-muted-foreground font-mono text-xs">
{NumberToHexPadded(otaLog.id, 8)}
</Table.Cell>
<Table.Cell class="text-sm" title={otaLog.startedAt.toLocaleString()}>
{formatRelativeTime(otaLog.startedAt)}
</Table.Cell>
<Table.Cell>
<Badge variant={getStatusBadgeVariant(otaLog.status)}>
{otaLog.status}
</Badge>
</Table.Cell>
<Table.Cell class="font-mono text-sm">{otaLog.version}</Table.Cell>
<Table.Cell class="text-muted-foreground max-w-xs truncate text-sm">
{otaLog.message ?? '-'}
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</div>
{/if}
</Card.Content>
</Card.Root>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(app)/profile/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</script>

<Container>
<h1 class="text-3xl font-bold">Profile</h1>
<h1 class="hidden text-3xl font-bold sm:block">Profile</h1>
<div
class="border-border bg-card flex w-full flex-col items-center justify-center gap-3 rounded-lg border border-dashed py-20"
>
Expand Down
36 changes: 15 additions & 21 deletions src/routes/(app)/settings/api-tokens/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import RotateCcw from '@lucide/svelte/icons/rotate-ccw';
import type { SortingState } from '@tanstack/table-core';
import type { ColumnDef } from '@tanstack/table-core';
import { Container } from '@openshock/svelte-core/components/index.js';
import { Container, PageHeader } from '@openshock/svelte-core/components/index.js';
import {
CreateActionsColumnDef,
CreateColumnDef,
Expand All @@ -17,7 +17,6 @@
} from '$lib/components/Table/ColumnUtils';
import DataTable from '$lib/components/Table/DataTableTemplate.svelte';
import { Button } from '@openshock/svelte-core/components/ui/button/index.js';
import * as Card from '@openshock/svelte-core/components/ui/card/index.js';
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
Expand Down Expand Up @@ -73,23 +72,18 @@
</script>

<Container>
<Card.Header class="w-full">
<Card.Title class="flex items-center justify-between space-x-2 text-3xl">
API Tokens
<div>
<Button href={resolve('/settings/api-tokens/new')}>
<Plus />
Generate Token
</Button>
<Button onclick={refresh}>
<RotateCcw />
Refresh
</Button>
</div>
</Card.Title>
<Card.Description>API Tokens are used to authenticate with the OpenShock API</Card.Description>
</Card.Header>
<Card.Content class="flex w-full flex-col space-y-4">
<DataTable data={tokens} {columns} {sorting} />
</Card.Content>
<PageHeader
title="API Tokens"
subtitle="API Tokens are used to authenticate with the OpenShock API"
>
<Button href={resolve('/settings/api-tokens/new')}>
<Plus />
Generate Token
</Button>
<Button onclick={refresh}>
<RotateCcw />
Refresh
</Button>
</PageHeader>
<DataTable data={tokens} {columns} {sorting} />
</Container>
2 changes: 1 addition & 1 deletion src/routes/(app)/settings/api-tokens/new/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
</script>

<Container class="items-center-safe justify-center-safe overflow-y-auto">
<Card.Root class="w-lg max-w-2xl shrink-0">
<Card.Root class="w-full max-w-2xl shrink-0">
{#if parseError}
<Card.Header>
<Card.Title class="text-2xl">API Token Request</Card.Title>
Expand Down
33 changes: 13 additions & 20 deletions src/routes/(app)/settings/connections/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
import { page } from '$app/state';
import type { OAuthConnectionResponse } from '$lib/api';
import { GetOAuthAuthorizeUrl } from '$lib/api/next/oauth';
import { Container } from '@openshock/svelte-core/components/index.js';
import { Container, PageHeader, EmptyState } from '@openshock/svelte-core/components/index.js';
import { Button } from '@openshock/svelte-core/components/ui/button/index.js';
import * as Card from '@openshock/svelte-core/components/ui/card/index.js';
import { EmptyState } from '@openshock/svelte-core/components/index.js';
import * as Dropdown from '@openshock/svelte-core/components/ui/dropdown-menu/index.js';
import * as Separator from '@openshock/svelte-core/components/ui/separator/index.js';
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
Expand Down Expand Up @@ -97,22 +95,17 @@
</script>

<Container>
<Card.Header class="w-full">
<Card.Title class="flex items-center justify-between text-3xl">
OAuth Connections
<div class="flex items-center gap-2">
<Button variant="ghost" onclick={refresh} disabled={loading} aria-busy={loading}>
<RotateCcw class="mr-2 size-4" />
{loading ? 'Refreshing…' : 'Refresh'}
</Button>
</div>
</Card.Title>
<Card.Description>
Link or unlink third-party accounts to sign in faster and keep your profile in sync.
</Card.Description>
</Card.Header>

<Card.Content class="flex w-full flex-1 flex-col space-y-6">
<PageHeader
title="OAuth Connections"
subtitle="Link or unlink third-party accounts to sign in faster and keep your profile in sync."
>
<Button variant="outline" onclick={refresh} disabled={loading} aria-busy={loading}>
<RotateCcw class="size-4" />
{loading ? 'Refreshing…' : 'Refresh'}
</Button>
</PageHeader>

<div class="w-full space-y-6">
<!-- Quick actions -->
<div class="flex flex-wrap gap-2">
<Dropdown.Root>
Expand Down Expand Up @@ -194,7 +187,7 @@
{/each}
</div>
{/if}
</Card.Content>
</div>
</Container>

<!-- Confirm dialog lives once at root -->
Expand Down
88 changes: 30 additions & 58 deletions src/routes/(app)/settings/sessions/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,93 +1,65 @@
<script lang="ts">
import { sessionsListSessions } from '$lib/api';
import { sessionsListSessions, sessionsGetSelfSession } from '$lib/api';
import type { LoginSessionResponse } from '$lib/api';
import RotateCcw from '@lucide/svelte/icons/rotate-ccw';
import type { ColumnDef, SortingState } from '@tanstack/table-core';
import { Container } from '@openshock/svelte-core/components/index.js';
import {
CreateActionsColumnDef,
CreateColumnDef,
CreateSortableColumnDef,
RenderCell,
TimeSinceRelativeOrNeverRenderer,
TimeSinceRelativeRenderer,
UserAgentRenderer,
} from '$lib/components/Table/ColumnUtils';
import DataTable from '$lib/components/Table/DataTableTemplate.svelte';
import { Button } from '@openshock/svelte-core/components/ui/button/index.js';
import * as Card from '@openshock/svelte-core/components/ui/card/index.js';
import { Container, PageHeader } from '@openshock/svelte-core/components/index.js';
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte';
import DataTableActions from './data-table-actions.svelte';
import SessionCard from './session-card.svelte';

registerBreadcrumbs(() => [
{ label: 'Settings', href: '/settings/account' },
{ label: 'Sessions' },
]);

let data = $state<LoginSessionResponse[]>([]);
let sorting = $state<SortingState>([]);
let sessions = $state<LoginSessionResponse[]>([]);
let currentSessionId = $state<string | null>(null);

function onRevoked(sessionId: string) {
const idx = data.findIndex((session) => session.id === sessionId);
if (idx === -1) return;

data.splice(idx, 1);
const idx = sessions.findIndex((s) => s.id === sessionId);
if (idx !== -1) sessions.splice(idx, 1);
}

const columns: ColumnDef<LoginSessionResponse>[] = [
CreateColumnDef('ip', 'Ip', RenderCell),
CreateSortableColumnDef('userAgent', 'User Agent', UserAgentRenderer),
CreateSortableColumnDef('created', 'Created', TimeSinceRelativeRenderer),
CreateSortableColumnDef('expires', 'Expires', TimeSinceRelativeRenderer),
CreateSortableColumnDef('lastUsed', 'Last seen', TimeSinceRelativeOrNeverRenderer),
CreateActionsColumnDef(DataTableActions, (session) => ({ session, onRevoked })),
];
// Sort so current session is always first
const sortedSessions = $derived(
[...sessions].sort((a, b) => {
if (a.id === currentSessionId) return -1;
if (b.id === currentSessionId) return 1;
return 0;
})
);

async function fetchSessions() {
try {
data = await sessionsListSessions();
[sessions, { id: currentSessionId }] = await Promise.all([
sessionsListSessions(),
sessionsGetSelfSession(),
]);
} catch (error) {
await handleApiError(error);
}
}

async function onRefreshClicked() {
await fetchSessions();
toast.success('Sessions refreshed successfully');
}

onMount(() => {
fetchSessions();

// Update timestamps every 5 seconds
const interval = setInterval(() => {
if (data) {
data = Object.assign([], data);
}
if (sessions.length) sessions = [...sessions];
}, 5000);

return () => clearInterval(interval);
});
</script>

<Container>
<Card.Header class="w-full">
<Card.Title class="flex items-center justify-between space-x-2 text-3xl">
Sessions
<Button class="text-xl" onclick={onRefreshClicked}>
<RotateCcw />
<span> Refresh </span>
</Button>
</Card.Title>
<Card.Description>
This is a list of all active sessions of your account. Revoke any sessions you do not
recognize.
</Card.Description>
</Card.Header>
<Card.Content class="w-full">
<DataTable {data} {columns} {sorting} />
</Card.Content>
<PageHeader
title="Sessions"
subtitle="This is a list of all active sessions of your account. Revoke any sessions you do not recognize."
/>

<div class="flex w-full flex-col gap-3">
{#each sortedSessions as session (session.id)}
<SessionCard {session} isCurrent={session.id === currentSessionId} {onRevoked} />
{/each}
</div>
</Container>
39 changes: 0 additions & 39 deletions src/routes/(app)/settings/sessions/data-table-actions.svelte

This file was deleted.

Loading
Loading