Skip to content
Merged

Daily #500

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
6 changes: 3 additions & 3 deletions apps/backgrounds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@next/third-parties": "16.1.2",
"@next/third-parties": "16.1.3",
"@react-three/drei": "^10.0.7",
"@react-three/fiber": "9.1.2",
"clsx": "^2.1.1",
"motion": "^12.11.0",
"next": "16.1.2",
"next": "16.1.3",
"react": "19.2.3",
"react-dom": "19.2.3",
"shadergradient": "^1.2.14",
Expand All @@ -31,7 +31,7 @@
"@types/react-dom": "^19",
"@types/three": "^0.170.0",
"eslint": "^9",
"eslint-config-next": "16.1.2",
"eslint-config-next": "16.1.3",
"tailwindcss": "^4",
"typescript": "^5"
}
Expand Down
8 changes: 4 additions & 4 deletions apps/viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"packageManager": "pnpm@10.10.0",
"dependencies": {
"@uidotdev/usehooks": "^2.4.1",
"lucide-react": "^0.511.0",
"next": "16.1.2",
"next": "16.1.3",
"pdfjs-dist": "4.8.69",
"react": "19.2.3",
"react-dom": "19.2.3",
Expand All @@ -27,9 +26,10 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.1.2",
"eslint-config-next": "16.1.3",
"postcss": "^8",
"tailwindcss": "^4",
"typescript": "^5"
}
},
"packageManager": "pnpm@10.10.0"
}
7 changes: 7 additions & 0 deletions database/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export type Database = MergeDeep<
Row: DatabaseGenerated["public"]["Tables"]["customer"]["Row"] & {
tags: string[];
};
/**
* `customer_with_tags` is a view with an INSTEAD OF INSERT trigger.
* We model Insert so client code can insert without `any` casts.
*/
Insert: DatabaseGenerated["public"]["Tables"]["customer"]["Insert"] & {
tags?: string[] | null;
};
};
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,44 @@ export async function POST(
.select("lang")
.eq("form_id", ctx.form.id)
.single();

if (!formDoc) {
return NextResponse.json({ error: "form not found" }, { status: 404 });
}

// Resolve brand info for email sender/display.
// Prefer published tenant branding (`www.title` / `www.publisher`) when available,
// otherwise fall back to a generic placeholder. (Do not expose internal project names.)
const { data: www_list, error: www_err } = await service_role.www
.from("www")
.select("title, publisher, lang")
.eq("project_id", ctx.form.project_id)
.limit(1);

const www = !www_err && www_list && www_list.length > 0 ? www_list[0] : null;

const brand_name =
www && typeof www.title === "string" && www.title
? String(www.title)
: "(Untitled)";

const publisher =
www && typeof www.publisher === "string" && www.publisher
? String(www.publisher)
: "";
const brand_support_url =
publisher.startsWith("http://") || publisher.startsWith("https://")
? publisher
: undefined;
const brand_support_contact = publisher.includes("@") ? publisher : undefined;

const langCandidate =
www && typeof www.lang === "string" && www.lang ? www.lang : formDoc.lang;
const emailLang: CIAMVerificationEmailLang = select_lang(
(formDoc as { lang?: unknown } | null)?.lang,
langCandidate,
supported_languages,
"en"
);

const brand_name = "Grida";
const { error: resend_err } = await resend.emails.send({
from: `${brand_name} <no-reply@accounts.grida.co>`,
to: email,
Expand All @@ -138,6 +169,8 @@ export async function POST(
brand_name,
expires_in_minutes,
lang: emailLang,
brand_support_url,
brand_support_contact,
}),
});

Expand Down
162 changes: 162 additions & 0 deletions editor/app/(dev)/ui/components/tags/_page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"use client";

import React, { useState } from "react";
import { TagInput } from "@/components/tag";
import { ComponentDemo } from "../component-demo";

export default function TagsPage() {
const [tags, setTags] = useState<{ id: string; text: string }[]>([
{ id: "react", text: "react" },
{ id: "typescript", text: "typescript" },
]);
const [tagsWithAutocomplete, setTagsWithAutocomplete] = useState<
{ id: string; text: string }[]
>([]);
const [emptyTags, setEmptyTags] = useState<{ id: string; text: string }[]>(
[]
);
const [coloredTags, setColoredTags] = useState<
{ id: string; text: string; color?: string }[]
>([{ id: "urgent", text: "urgent", color: "#ef4444" }]);

const autocompleteOptions = [
{ id: "apple", text: "apple" },
{ id: "banana", text: "banana" },
{ id: "cherry", text: "cherry" },
{ id: "date", text: "date" },
{ id: "elderberry", text: "elderberry" },
{ id: "fig", text: "fig" },
{ id: "grape", text: "grape" },
{ id: "honeydew", text: "honeydew" },
{ id: "kiwi", text: "kiwi" },
{ id: "lemon", text: "lemon" },
{ id: "mango", text: "mango" },
{ id: "orange", text: "orange" },
{ id: "papaya", text: "papaya" },
{ id: "raspberry", text: "raspberry" },
{ id: "strawberry", text: "strawberry" },
];

const coloredAutocompleteOptions = [
{ id: "urgent", text: "urgent", color: "#ef4444" },
{ id: "review", text: "review", color: "#f59e0b" },
{ id: "planned", text: "planned", color: "#3b82f6" },
{ id: "done", text: "done", color: "#22c55e" },
{ id: "blocked", text: "blocked", color: "#a855f7" },
];

return (
<main className="container max-w-screen-lg mx-auto py-10">
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold mb-2">Tag Input</h1>
<p className="text-gray-600">
A flexible tag input component with autocomplete support for
managing multiple values.
</p>
</div>

<hr />

<section className="space-y-4">
<div>
<h2 className="text-xl font-semibold mb-1">Basic Usage</h2>
<p className="text-sm text-gray-600">
Create and manage tags with keyboard support
</p>
</div>
<ComponentDemo
notes={
<>
<strong>Tips:</strong> Type and press Enter to add tags. Click
on a tag to remove it.
</>
Comment thread
softmarshmallow marked this conversation as resolved.
}
>
<TagInput
tags={tags}
setTags={setTags}
activeTagIndex={null}
setActiveTagIndex={() => {}}
/>
Comment on lines +76 to +81
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Wire activeTagIndex state so keyboard navigation works in the demos.

With activeTagIndex={null} and a no‑op setter, arrow navigation and Delete/Backspace removal won’t work, which undercuts the “keyboard support” demo. Consider local state per demo instance.

🛠️ Suggested wiring
-import React, { useState } from "react";
+import React, { useState } from "react";

 export default function TagsPage() {
+  const [basicActiveIndex, setBasicActiveIndex] = useState<number | null>(null);
+  const [autoActiveIndex, setAutoActiveIndex] = useState<number | null>(null);
+  const [colorActiveIndex, setColorActiveIndex] = useState<number | null>(null);
+  const [emptyActiveIndex, setEmptyActiveIndex] = useState<number | null>(null);
   const [tags, setTags] = useState<{ id: string; text: string }[]>([
     { id: "react", text: "react" },
     { id: "typescript", text: "typescript" },
   ]);
   ...
             <TagInput
               tags={tags}
               setTags={setTags}
-              activeTagIndex={null}
-              setActiveTagIndex={() => {}}
+              activeTagIndex={basicActiveIndex}
+              setActiveTagIndex={setBasicActiveIndex}
             />
   ...
             <TagInput
               tags={tagsWithAutocomplete}
               setTags={setTagsWithAutocomplete}
               enableAutocomplete
               autocompleteOptions={autocompleteOptions}
-              activeTagIndex={null}
-              setActiveTagIndex={() => {}}
+              activeTagIndex={autoActiveIndex}
+              setActiveTagIndex={setAutoActiveIndex}
             />
   ...
             <TagInput
               tags={coloredTags}
               setTags={setColoredTags}
               enableAutocomplete
               autocompleteOptions={coloredAutocompleteOptions}
-              activeTagIndex={null}
-              setActiveTagIndex={() => {}}
+              activeTagIndex={colorActiveIndex}
+              setActiveTagIndex={setColorActiveIndex}
             />
   ...
             <TagInput
               tags={emptyTags}
               setTags={setEmptyTags}
-              activeTagIndex={null}
-              setActiveTagIndex={() => {}}
+              activeTagIndex={emptyActiveIndex}
+              setActiveTagIndex={setEmptyActiveIndex}
             />

Also applies to: 102-109, 130-137, 151-156

🤖 Prompt for AI Agents
In `@editor/app/`(dev)/ui/components/tags/_page.tsx around lines 76 - 81, The
demos pass a constant activeTagIndex and a no-op setter to TagInput which
disables keyboard navigation; replace those with local state using
useState<number | null> (e.g., const [activeTagIndex, setActiveTagIndex] =
useState<number | null>(null)) inside each demo component and pass both
activeTagIndex and setActiveTagIndex into TagInput (replace the hardcoded
activeTagIndex={null} and setActiveTagIndex={() => {}}). Apply the same change
for the other demo instances that render TagInput (the blocks around the other
mentioned ranges) so arrow keys and Delete/Backspace work as intended.

</ComponentDemo>
</section>

<hr />

<section className="space-y-4">
<div>
<h2 className="text-xl font-semibold mb-1">With Autocomplete</h2>
<p className="text-sm text-gray-600">
Enable autocomplete suggestions for faster input
</p>
</div>
<ComponentDemo
notes={
<>
<strong>Tips:</strong> Start typing to see autocomplete
suggestions. Select from the dropdown or create custom tags.
</>
}
>
<TagInput
tags={tagsWithAutocomplete}
setTags={setTagsWithAutocomplete}
enableAutocomplete
autocompleteOptions={autocompleteOptions}
activeTagIndex={null}
setActiveTagIndex={() => {}}
/>
</ComponentDemo>
</section>

<hr />

<section className="space-y-4">
<div>
<h2 className="text-xl font-semibold mb-1">With Colors</h2>
<p className="text-sm text-gray-600">
Autocomplete and selected tags can be color-coded.
</p>
</div>
<ComponentDemo
notes={
<>
<strong>Tips:</strong> Pick from the colored suggestions to see
the chips tinted, Notion-style.
</>
}
>
<TagInput
tags={coloredTags}
setTags={setColoredTags}
enableAutocomplete
autocompleteOptions={coloredAutocompleteOptions}
activeTagIndex={null}
setActiveTagIndex={() => {}}
/>
</ComponentDemo>
</section>

<hr />

<section className="space-y-4">
<div>
<h2 className="text-xl font-semibold mb-1">Empty State</h2>
<p className="text-sm text-gray-600">
Tag input without any initial values
</p>
</div>
<ComponentDemo notes="Start typing to add your first tag.">
<TagInput
tags={emptyTags}
setTags={setEmptyTags}
activeTagIndex={null}
setActiveTagIndex={() => {}}
/>
</ComponentDemo>
</section>
</div>
</main>
);
}
Loading