update rules
This commit is contained in:
parent
82d3d4f933
commit
8a5c3da385
5 changed files with 153 additions and 11 deletions
|
|
@ -34,6 +34,53 @@ export const create = mutation({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const update = mutation({
|
||||||
|
args: {
|
||||||
|
ruleId: v.id('user_rules'),
|
||||||
|
attach: ruleAttachValidator,
|
||||||
|
rule: v.string(),
|
||||||
|
sessionToken: v.string(),
|
||||||
|
},
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
const session = await ctx.runQuery(internal.betterAuth.getSession, {
|
||||||
|
sessionToken: args.sessionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!session) throw new Error('Invalid session token');
|
||||||
|
|
||||||
|
const existing = await ctx.db.get(args.ruleId);
|
||||||
|
|
||||||
|
if (!existing) throw new Error('Rule not found');
|
||||||
|
if (existing.user_id !== session.userId) throw new Error('You are not the owner of this rule');
|
||||||
|
|
||||||
|
await ctx.db.patch(args.ruleId, {
|
||||||
|
attach: args.attach,
|
||||||
|
rule: args.rule,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const remove = mutation({
|
||||||
|
args: {
|
||||||
|
ruleId: v.id('user_rules'),
|
||||||
|
sessionToken: v.string(),
|
||||||
|
},
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
const session = await ctx.runQuery(internal.betterAuth.getSession, {
|
||||||
|
sessionToken: args.sessionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!session) throw new Error('Invalid session token');
|
||||||
|
|
||||||
|
const existing = await ctx.db.get(args.ruleId);
|
||||||
|
|
||||||
|
if (!existing) throw new Error('Rule not found');
|
||||||
|
if (existing.user_id !== session.userId) throw new Error('You are not the owner of this rule');
|
||||||
|
|
||||||
|
await ctx.db.delete(args.ruleId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const all = query({
|
export const all = query({
|
||||||
args: {
|
args: {
|
||||||
sessionToken: v.string(),
|
sessionToken: v.string(),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
import Label from './label.svelte';
|
import Label from './label.svelte';
|
||||||
|
|
||||||
export { Label };
|
export { Label };
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
import Textarea from './textarea.svelte';
|
import Textarea from './textarea.svelte';
|
||||||
|
|
||||||
export { Textarea };
|
export { Textarea };
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
import type { Doc } from '$lib/backend/convex/_generated/dataModel';
|
import type { Doc } from '$lib/backend/convex/_generated/dataModel';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { api } from '$lib/backend/convex/_generated/api';
|
import { api } from '$lib/backend/convex/_generated/api';
|
||||||
|
import Rule from './rule.svelte';
|
||||||
|
|
||||||
const client = useConvexClient();
|
const client = useConvexClient();
|
||||||
|
|
||||||
|
|
@ -28,9 +29,9 @@
|
||||||
async function submitNewRule(e: SubmitEvent) {
|
async function submitNewRule(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(e.target as HTMLFormElement);
|
const formData = new FormData(e.target as HTMLFormElement);
|
||||||
const name = formData.get('name');
|
const name = formData.get('name') as string;
|
||||||
const attach = formData.get('attach');
|
const attach = formData.get('attach') as 'always' | 'manual';
|
||||||
const rule = formData.get('rule');
|
const rule = formData.get('rule') as string;
|
||||||
|
|
||||||
if (rule === '' || !rule) return;
|
if (rule === '' || !rule) return;
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@
|
||||||
|
|
||||||
<div class="mt-8 flex flex-col gap-4">
|
<div class="mt-8 flex flex-col gap-4">
|
||||||
<div class="flex place-items-center justify-between">
|
<div class="flex place-items-center justify-between">
|
||||||
<h3 class="text-lg font-bold">Rules</h3>
|
<h3 class="text-xl font-bold">Rules</h3>
|
||||||
<Button
|
<Button
|
||||||
{...newRuleCollapsible.trigger}
|
{...newRuleCollapsible.trigger}
|
||||||
variant={newRuleCollapsible.open ? 'outline' : 'default'}
|
variant={newRuleCollapsible.open ? 'outline' : 'default'}
|
||||||
|
|
@ -76,6 +77,7 @@
|
||||||
<div
|
<div
|
||||||
{...newRuleCollapsible.content}
|
{...newRuleCollapsible.content}
|
||||||
in:slide={{ duration: 150, axis: 'y' }}
|
in:slide={{ duration: 150, axis: 'y' }}
|
||||||
|
out:slide={{ duration: 150, axis: 'y' }}
|
||||||
class="bg-card flex flex-col gap-4 rounded-lg border p-4"
|
class="bg-card flex flex-col gap-4 rounded-lg border p-4"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
|
|
@ -111,10 +113,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each userRulesQuery.data ?? [] as rule}
|
{#each userRulesQuery.data ?? [] as rule (rule._id)}
|
||||||
<div class="flex flex-col gap-2">
|
<Rule {rule} />
|
||||||
<h3 class="text-lg font-bold">{rule.name}</h3>
|
|
||||||
<p class="text-muted-foreground text-sm">{rule.rule}</p>
|
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
96
src/routes/account/customization/rule.svelte
Normal file
96
src/routes/account/customization/rule.svelte
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import * as Card from '$lib/components/ui/card';
|
||||||
|
import { Label } from '$lib/components/ui/label';
|
||||||
|
import { Textarea } from '$lib/components/ui/textarea';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import type { Doc } from '$lib/backend/convex/_generated/dataModel';
|
||||||
|
import { useConvexClient } from 'convex-svelte';
|
||||||
|
import { api } from '$lib/backend/convex/_generated/api';
|
||||||
|
import { session } from '$lib/state/session.svelte';
|
||||||
|
import { LocalToasts } from '$lib/builders/local-toasts.svelte';
|
||||||
|
import { ResultAsync } from 'neverthrow';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
rule: Doc<'user_rules'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const id = $props.id();
|
||||||
|
|
||||||
|
let { rule }: Props = $props();
|
||||||
|
|
||||||
|
const client = useConvexClient();
|
||||||
|
|
||||||
|
let updating = $state(false);
|
||||||
|
|
||||||
|
const toasts = new LocalToasts({ id });
|
||||||
|
|
||||||
|
async function updateRule(e: SubmitEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(e.target as HTMLFormElement);
|
||||||
|
const attach = formData.get('attach') as 'always' | 'manual';
|
||||||
|
const ruleText = formData.get('rule') as string;
|
||||||
|
|
||||||
|
if (ruleText === '' || !ruleText) return;
|
||||||
|
|
||||||
|
updating = true;
|
||||||
|
|
||||||
|
const res = await ResultAsync.fromPromise(
|
||||||
|
client.mutation(api.user_rules.update, {
|
||||||
|
ruleId: rule._id,
|
||||||
|
attach,
|
||||||
|
rule: ruleText,
|
||||||
|
sessionToken: session.current?.session.token ?? '',
|
||||||
|
}),
|
||||||
|
(e) => e
|
||||||
|
);
|
||||||
|
|
||||||
|
toasts.addToast({
|
||||||
|
data: {
|
||||||
|
content: res.isOk() ? 'Saved' : 'Failed to save',
|
||||||
|
variant: res.isOk() ? 'info' : 'danger',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
updating = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card.Root>
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title>{rule.name}</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Content tag="form" onsubmit={updateRule}>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label for="attach">Attach</Label>
|
||||||
|
<select
|
||||||
|
id="attach"
|
||||||
|
name="attach"
|
||||||
|
value={rule.attach}
|
||||||
|
class="border-input bg-background h-9 w-fit rounded-md border px-2 pr-6 text-sm"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="always">Always</option>
|
||||||
|
<option value="manual">Manual</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label for="rule">Instructions</Label>
|
||||||
|
<Textarea
|
||||||
|
id="rule"
|
||||||
|
value={rule.rule}
|
||||||
|
name="rule"
|
||||||
|
placeholder="How should the AI respond?"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<Button loading={updating} {...toasts.trigger} type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</Card.Content>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
{#each toasts.toasts as toast (toast)}
|
||||||
|
<div {...toast.attrs} class={toast.class}>
|
||||||
|
{toast.data.content}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
Loading…
Add table
Reference in a new issue