Skip to content

Commit dce10d8

Browse files
committed
polished UI a lot
1 parent 3174312 commit dce10d8

File tree

14 files changed

+1458
-812
lines changed

14 files changed

+1458
-812
lines changed

apps/web/src/convex/resources.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,34 @@ export const listGlobal = query({
1919
});
2020

2121
/**
22-
* List user resources for the authenticated user's instance
22+
* List user resources for the authenticated user's instance, optionally filtered by project
2323
*/
2424
export const listUserResources = query({
25-
args: {},
26-
handler: async (ctx) => {
25+
args: {
26+
projectId: v.optional(v.id('projects'))
27+
},
28+
handler: async (ctx, args) => {
2729
const instance = await getAuthenticatedInstance(ctx);
2830

29-
return await ctx.db
31+
if (args.projectId) {
32+
const resources = await ctx.db
33+
.query('userResources')
34+
.withIndex('by_project', (q) => q.eq('projectId', args.projectId))
35+
.collect();
36+
return resources.filter((r) => r.instanceId === instance._id);
37+
}
38+
39+
const allResources = await ctx.db
3040
.query('userResources')
3141
.withIndex('by_instance', (q) => q.eq('instanceId', instance._id))
3242
.collect();
43+
44+
const seen = new Set<string>();
45+
return allResources.filter((r) => {
46+
if (seen.has(r.name)) return false;
47+
seen.add(r.name);
48+
return true;
49+
});
3350
}
3451
});
3552

@@ -119,13 +136,15 @@ export const addCustomResource = mutation({
119136
url: v.string(),
120137
branch: v.string(),
121138
searchPath: v.optional(v.string()),
122-
specialNotes: v.optional(v.string())
139+
specialNotes: v.optional(v.string()),
140+
projectId: v.optional(v.id('projects'))
123141
},
124142
handler: async (ctx, args) => {
125143
const instance = await getAuthenticatedInstance(ctx);
126144

127145
const resourceId = await ctx.db.insert('userResources', {
128146
instanceId: instance._id,
147+
projectId: args.projectId,
129148
name: args.name,
130149
type: 'git',
131150
url: args.url,

apps/web/src/convex/threads.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,28 @@ import { AnalyticsEvents } from './analyticsEvents';
66
import { getAuthenticatedInstance, requireThreadOwnership } from './authHelpers';
77

88
/**
9-
* List threads for the authenticated user's instance
9+
* List threads for the authenticated user's instance, optionally filtered by project
1010
*/
1111
export const list = query({
12-
args: {},
13-
handler: async (ctx) => {
12+
args: {
13+
projectId: v.optional(v.id('projects'))
14+
},
15+
handler: async (ctx, args) => {
1416
const instance = await getAuthenticatedInstance(ctx);
1517

16-
const threads = await ctx.db
17-
.query('threads')
18-
.withIndex('by_instance', (q) => q.eq('instanceId', instance._id))
19-
.collect();
18+
let threads;
19+
if (args.projectId) {
20+
threads = await ctx.db
21+
.query('threads')
22+
.withIndex('by_project', (q) => q.eq('projectId', args.projectId))
23+
.collect();
24+
threads = threads.filter((t) => t.instanceId === instance._id);
25+
} else {
26+
threads = await ctx.db
27+
.query('threads')
28+
.withIndex('by_instance', (q) => q.eq('instanceId', instance._id))
29+
.collect();
30+
}
2031

2132
const activeStreamSessions = await ctx.db
2233
.query('streamSessions')
@@ -79,13 +90,15 @@ export const getWithMessages = query({
7990
*/
8091
export const create = mutation({
8192
args: {
82-
title: v.optional(v.string())
93+
title: v.optional(v.string()),
94+
projectId: v.optional(v.id('projects'))
8395
},
8496
handler: async (ctx, args) => {
8597
const instance = await getAuthenticatedInstance(ctx);
8698

8799
const threadId = await ctx.db.insert('threads', {
88100
instanceId: instance._id,
101+
projectId: args.projectId,
89102
title: args.title,
90103
createdAt: Date.now(),
91104
lastActivityAt: Date.now()
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<script lang="ts">
2+
import { Loader2 } from '@lucide/svelte';
3+
import { getProjectStore } from '$lib/stores/project.svelte';
4+
5+
const projectStore = getProjectStore();
6+
7+
let newProjectName = $state('');
8+
let isCreating = $state(false);
9+
10+
function closeModal() {
11+
projectStore.showCreateModal = false;
12+
newProjectName = '';
13+
}
14+
15+
async function createProject() {
16+
if (!newProjectName.trim()) return;
17+
isCreating = true;
18+
const projectId = await projectStore.createProject(newProjectName.trim());
19+
isCreating = false;
20+
if (projectId) {
21+
await projectStore.selectProjectWithNavigation(projectId);
22+
closeModal();
23+
}
24+
}
25+
</script>
26+
27+
{#if projectStore.showCreateModal}
28+
<div
29+
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
30+
role="dialog"
31+
aria-modal="true"
32+
tabindex="-1"
33+
onclick={closeModal}
34+
onkeydown={(e) => e.key === 'Escape' && closeModal()}
35+
>
36+
<div
37+
class="bc-card w-full max-w-md p-6"
38+
role="document"
39+
onclick={(e) => e.stopPropagation()}
40+
onkeydown={(e) => e.stopPropagation()}
41+
>
42+
<h2 class="mb-4 text-lg font-semibold">Create New Project</h2>
43+
44+
<form
45+
onsubmit={(e) => {
46+
e.preventDefault();
47+
createProject();
48+
}}
49+
>
50+
<div class="mb-4">
51+
<label for="project-name" class="mb-1 block text-sm font-medium">Project Name</label>
52+
<input
53+
id="project-name"
54+
type="text"
55+
class="bc-input w-full"
56+
placeholder="my-project"
57+
bind:value={newProjectName}
58+
disabled={isCreating}
59+
/>
60+
</div>
61+
62+
{#if projectStore.error}
63+
<div class="mb-4 text-sm text-red-500">
64+
{projectStore.error}
65+
</div>
66+
{/if}
67+
68+
<div class="flex justify-end gap-2">
69+
<button type="button" class="bc-btn text-xs" onclick={closeModal} disabled={isCreating}>
70+
Cancel
71+
</button>
72+
<button
73+
type="submit"
74+
class="bc-btn bc-btn-primary text-xs"
75+
disabled={!newProjectName.trim() || isCreating}
76+
>
77+
{#if isCreating}
78+
<Loader2 size={14} class="animate-spin" />
79+
Creating...
80+
{:else}
81+
Create
82+
{/if}
83+
</button>
84+
</div>
85+
</form>
86+
</div>
87+
</div>
88+
{/if}

apps/web/src/lib/components/ProjectSelector.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
}
2121
}
2222
23-
function selectProject(projectId: string | null) {
24-
projectStore.selectProject(projectId as any);
23+
async function selectProject(projectId: string | null) {
24+
await projectStore.selectProjectWithNavigation(projectId as any);
2525
isOpen = false;
2626
}
2727
@@ -33,7 +33,7 @@
3333
isCreating = false;
3434
3535
if (projectId) {
36-
selectProject(projectId);
36+
await selectProject(projectId);
3737
showCreateModal = false;
3838
newProjectName = '';
3939
}

0 commit comments

Comments
 (0)