Skip to content

Commit 98bd94f

Browse files
committed
refactor: flatten left sidebar cards and move right sidebar header to PageLayout slot
- Remove Card/CardHeader/CardTitle wrappers from FavoritePagesCard, RecentPagesCard, and SelectedPagesCard - Replace with simple div headers using font-medium styling and mb-2 spacing - Move "Path" card content in PageEditorView to unwrapped div with consistent text-xs styling - Extract right sidebar header ("Note/Arrow/Page Properties") from PageEditorView to new right-sidebar-header slot in PageLayout - Remove
1 parent 718e13a commit 98bd94f

11 files changed

Lines changed: 368 additions & 219 deletions

File tree

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<script setup lang="ts">
2+
import { computed, ref, watch } from "vue";
3+
import { RouterLink } from "vue-router";
4+
5+
import { Button } from "@/components/ui/button";
6+
import {
7+
DropdownMenu,
8+
DropdownMenuContent,
9+
DropdownMenuTrigger,
10+
} from "@/components/ui/dropdown-menu";
11+
import { Bell, ArrowRight } from "@lucide/vue";
12+
13+
import { useNotifications } from "./useNotifications";
14+
import { unreadNotificationCount } from "./useNotificationBadge";
15+
16+
const open = ref(false);
17+
18+
const {
19+
rows,
20+
loadFirst,
21+
loadMore,
22+
markRead,
23+
loading,
24+
error,
25+
hasMore,
26+
markingRead,
27+
} = useNotifications();
28+
29+
const localUnreadCount = computed(
30+
() => rows.value.filter((r) => r.unread).length,
31+
);
32+
33+
watch(open, (isOpen) => {
34+
if (isOpen && rows.value.length === 0 && !loading.value) {
35+
void loadFirst();
36+
}
37+
});
38+
39+
function formatWhen(iso: string): string {
40+
const d = new Date(iso);
41+
if (Number.isNaN(d.getTime())) return iso;
42+
return new Intl.DateTimeFormat(undefined, {
43+
dateStyle: "short",
44+
timeStyle: "short",
45+
}).format(d);
46+
}
47+
</script>
48+
49+
<template>
50+
<DropdownMenu v-model:open="open">
51+
<DropdownMenuTrigger as-child>
52+
<Button
53+
variant="ghost"
54+
size="icon"
55+
class="relative h-8 w-8"
56+
>
57+
<Bell class="h-4 w-4" />
58+
<span
59+
v-if="unreadNotificationCount > 0"
60+
class="bg-primary text-primary-foreground absolute -right-0.5 -top-0.5 flex h-3.5 min-w-[0.875rem] items-center justify-center rounded-full px-0.5 text-[9px] font-bold"
61+
>
62+
{{ unreadNotificationCount > 99 ? "99+" : unreadNotificationCount }}
63+
</span>
64+
</Button>
65+
</DropdownMenuTrigger>
66+
67+
<DropdownMenuContent class="w-80 max-h-96 overflow-hidden p-0" align="end">
68+
<!-- Header -->
69+
<div class="flex items-center justify-between border-b px-3 py-2">
70+
<span class="text-sm font-semibold">Notifications</span>
71+
<Button
72+
v-if="localUnreadCount > 0"
73+
variant="ghost"
74+
size="sm"
75+
class="h-6 text-xs"
76+
:disabled="markingRead"
77+
@click="markRead()"
78+
>
79+
Mark all read
80+
</Button>
81+
</div>
82+
83+
<!-- Scrollable list -->
84+
<div class="max-h-72 overflow-y-auto px-3 py-2">
85+
<p v-if="loading && rows.length === 0" class="text-muted-foreground text-xs">
86+
Loading…
87+
</p>
88+
<p v-else-if="error" class="text-destructive text-xs">
89+
{{ error }}
90+
</p>
91+
<ul v-else-if="rows.length > 0" class="space-y-2">
92+
<li
93+
v-for="n in rows"
94+
:key="n.id"
95+
class="rounded-md border px-2 py-1.5 text-xs"
96+
:class="n.unread ? 'border-primary/30 bg-muted/40' : 'border-border/40'"
97+
>
98+
<div class="flex items-start justify-between gap-2">
99+
<span class="font-medium">{{ n.type }}</span>
100+
<span class="text-muted-foreground shrink-0">{{ formatWhen(n.dateTime) }}</span>
101+
</div>
102+
<p
103+
v-if="n.decryptedText"
104+
class="text-muted-foreground mt-0.5 line-clamp-2 leading-snug"
105+
>
106+
{{ n.decryptedText }}
107+
</p>
108+
<p v-else class="text-muted-foreground mt-0.5 text-[11px]">
109+
Encrypted payload
110+
</p>
111+
</li>
112+
</ul>
113+
<p v-else class="text-muted-foreground text-xs">
114+
No notifications.
115+
</p>
116+
117+
<div v-if="hasMore" class="mt-2 flex justify-center">
118+
<Button
119+
:disabled="loading"
120+
size="sm"
121+
variant="ghost"
122+
class="h-6 text-xs"
123+
@click="loadMore()"
124+
>
125+
{{ loading ? "Loading…" : "Load older" }}
126+
</Button>
127+
</div>
128+
</div>
129+
130+
<!-- Footer link -->
131+
<div class="border-t px-3 py-2">
132+
<RouterLink
133+
to="/notifications"
134+
class="text-primary flex items-center gap-1 text-xs hover:underline"
135+
@click="open = false"
136+
>
137+
View all notifications
138+
<ArrowRight class="h-3 w-3" />
139+
</RouterLink>
140+
</div>
141+
</DropdownMenuContent>
142+
</DropdownMenu>
143+
</template>
Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
22
import { computed } from 'vue'
3-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
43
import { Button } from '@/components/ui/button'
54
import { Star, X } from '@lucide/vue'
65
import { RouterLink } from 'vue-router'
@@ -23,12 +22,12 @@ function pageLabel(pid: string): string {
2322
</script>
2423

2524
<template>
26-
<Card>
27-
<CardHeader class="pb-2 flex flex-row items-center justify-between space-y-0">
28-
<CardTitle class="text-sm flex items-center gap-2">
25+
<div>
26+
<div class="mb-2 flex items-center justify-between">
27+
<div class="text-sm flex items-center gap-2 font-medium">
2928
<Star class="h-4 w-4" />
3029
Favorite Pages
31-
</CardTitle>
30+
</div>
3231
<Button
3332
variant="ghost"
3433
size="icon"
@@ -38,22 +37,20 @@ function pageLabel(pid: string): string {
3837
>
3938
<X class="h-3 w-3" />
4039
</Button>
41-
</CardHeader>
42-
<CardContent class="text-xs">
43-
<p v-if="favoritePageIds.length === 0" class="text-muted-foreground">
44-
No favorite pages.
45-
</p>
46-
<nav v-else class="space-y-1">
47-
<RouterLink
48-
v-for="pageId in favoritePageIds"
49-
:key="pageId"
50-
:to="`/pages/${pageId}`"
51-
class="block rounded-md px-2 py-1.5 transition-colors hover:bg-accent"
52-
:class="{ 'bg-accent': pageId === currentPageId }"
53-
>
54-
{{ pageLabel(pageId) }}
55-
</RouterLink>
56-
</nav>
57-
</CardContent>
58-
</Card>
40+
</div>
41+
<p v-if="favoritePageIds.length === 0" class="text-muted-foreground text-xs">
42+
No favorite pages.
43+
</p>
44+
<nav v-else class="space-y-1 text-xs">
45+
<RouterLink
46+
v-for="pageId in favoritePageIds"
47+
:key="pageId"
48+
:to="`/pages/${pageId}`"
49+
class="block rounded-md px-2 py-1.5 transition-colors hover:bg-accent"
50+
:class="{ 'bg-accent': pageId === currentPageId }"
51+
>
52+
{{ pageLabel(pageId) }}
53+
</RouterLink>
54+
</nav>
55+
</div>
5956
</template>

new-deepnotes/apps/web/src/features/pages/PageEditorView.vue

Lines changed: 51 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ import { computed, onMounted, ref, watch } from "vue";
33
import { RouterLink, useRoute, useRouter } from "vue-router";
44
55
import { Button } from "@/components/ui/button";
6-
import {
7-
Card,
8-
CardContent,
9-
CardHeader,
10-
CardTitle,
11-
} from "@/components/ui/card";
126
import { useSession } from "../auth/useSession";
137
import SpatialPageView from "../spatial/SpatialPageView.vue";
148
import { useUserTemplates } from "../spatial/useUserTemplates";
@@ -441,57 +435,50 @@ onMounted(() => {
441435
<template #left-sidebar>
442436
<PageEditorLeftSidebar v-slot="{ activeTab }">
443437
<!-- Current path -->
444-
<div v-if="activeTab === 'path'" class="space-y-3">
445-
<Card>
446-
<CardHeader class="pb-2">
447-
<CardTitle class="text-sm">Path</CardTitle>
448-
</CardHeader>
449-
<CardContent class="space-y-2 text-xs">
450-
<p v-if="pathLoading" class="text-muted-foreground">Loading…</p>
451-
<p v-else-if="pathError" class="text-destructive">{{ pathError }}</p>
452-
<nav v-else class="text-muted-foreground flex flex-wrap items-center gap-1">
453-
<template v-for="(pid, i) in pathPageIds" :key="pid">
454-
<span v-if="i > 0">/</span>
455-
<RouterLink
456-
v-if="i < pathPageIds.length - 1"
457-
class="text-primary hover:underline"
458-
:to="`/pages/${pid}`"
459-
>
460-
{{ pagePathLabel(pid, pageLabels) }}
461-
</RouterLink>
462-
<span v-else class="text-foreground font-medium">
463-
{{ pagePathLabel(pid, pageLabels) }}
464-
</span>
465-
</template>
466-
</nav>
467-
<div class="flex flex-wrap gap-1">
468-
<Button
469-
size="xs"
470-
variant="secondary"
471-
class="h-6 text-[10px]"
472-
:disabled="pagePrefsLoading"
473-
@click="bumpAsStarting()"
474-
>
475-
Make starting
476-
</Button>
477-
<Button
478-
size="xs"
479-
variant="outline"
480-
class="h-6 text-[10px]"
481-
:disabled="pagePrefsLoading"
482-
@click="toggleFavorite()"
483-
>
484-
{{ isFavorite ? "Unfavorite" : "Favorite" }}
485-
</Button>
486-
</div>
487-
<p v-if="bumpMessage" class="text-muted-foreground text-[10px]">
488-
{{ bumpMessage }}
489-
</p>
490-
<p v-if="favoriteMessage" class="text-amber-700 dark:text-amber-300 text-[10px]">
491-
{{ favoriteMessage }}
492-
</p>
493-
</CardContent>
494-
</Card>
438+
<div v-if="activeTab === 'path'" class="space-y-2 text-xs">
439+
<p v-if="pathLoading" class="text-muted-foreground">Loading…</p>
440+
<p v-else-if="pathError" class="text-destructive">{{ pathError }}</p>
441+
<nav v-else class="text-muted-foreground flex flex-wrap items-center gap-1">
442+
<template v-for="(pid, i) in pathPageIds" :key="pid">
443+
<span v-if="i > 0">/</span>
444+
<RouterLink
445+
v-if="i < pathPageIds.length - 1"
446+
class="text-primary hover:underline"
447+
:to="`/pages/${pid}`"
448+
>
449+
{{ pagePathLabel(pid, pageLabels) }}
450+
</RouterLink>
451+
<span v-else class="text-foreground font-medium">
452+
{{ pagePathLabel(pid, pageLabels) }}
453+
</span>
454+
</template>
455+
</nav>
456+
<div class="flex flex-wrap gap-1">
457+
<Button
458+
size="xs"
459+
variant="secondary"
460+
class="h-6 text-[10px]"
461+
:disabled="pagePrefsLoading"
462+
@click="bumpAsStarting()"
463+
>
464+
Make starting
465+
</Button>
466+
<Button
467+
size="xs"
468+
variant="outline"
469+
class="h-6 text-[10px]"
470+
:disabled="pagePrefsLoading"
471+
@click="toggleFavorite()"
472+
>
473+
{{ isFavorite ? "Unfavorite" : "Favorite" }}
474+
</Button>
475+
</div>
476+
<p v-if="bumpMessage" class="text-muted-foreground text-[10px]">
477+
{{ bumpMessage }}
478+
</p>
479+
<p v-if="favoriteMessage" class="text-amber-700 dark:text-amber-300 text-[10px]">
480+
{{ favoriteMessage }}
481+
</p>
495482
</div>
496483

497484
<!-- Recent pages -->
@@ -523,15 +510,16 @@ onMounted(() => {
523510
</PageEditorLeftSidebar>
524511
</template>
525512

513+
<!-- === Right sidebar header === -->
514+
<template #right-sidebar-header>
515+
<span v-if="selectedNoteId">Note Properties</span>
516+
<span v-else-if="selectedArrowId">Arrow Properties</span>
517+
<span v-else>Page Properties</span>
518+
</template>
519+
526520
<!-- === Right sidebar === -->
527521
<template #right-sidebar>
528522
<div class="space-y-3">
529-
<div class="border-border/40 border-b pb-2 text-xs font-semibold tracking-wide uppercase">
530-
<span v-if="selectedNoteId">Note Properties</span>
531-
<span v-else-if="selectedArrowId">Arrow Properties</span>
532-
<span v-else>Page Properties</span>
533-
</div>
534-
535523
<PagePropertiesCard
536524
v-if="!selectedNoteId && !selectedArrowId"
537525
:page-id="pageId"

0 commit comments

Comments
 (0)