UUID refactoring: project URLs by ID, mentions send member IDs, remove slug from JWT decode
This commit is contained in:
parent
407c065b49
commit
79541d133a
@ -21,7 +21,7 @@ function App() {
|
||||
</AuthGuard>
|
||||
} />
|
||||
|
||||
<Route path="/projects/:slug" element={
|
||||
<Route path="/projects/:id" element={
|
||||
<AuthGuard>
|
||||
<ProjectPage />
|
||||
</AuthGuard>
|
||||
|
||||
@ -38,6 +38,7 @@ function isImageMime(mime: string | null): boolean {
|
||||
export default function ChatPanel({ chatId, projectId }: Props) {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [input, setInput] = useState("");
|
||||
const mentionMapRef = useRef<Map<string, string>>(new Map()); // slug → member_id
|
||||
const [sending, setSending] = useState(false);
|
||||
const [loadingOlder, setLoadingOlder] = useState(false);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
@ -197,11 +198,15 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
||||
}));
|
||||
setInput("");
|
||||
setPendingFiles([]);
|
||||
mentionMapRef.current.clear();
|
||||
setSending(true);
|
||||
try {
|
||||
// Extract @mentions from text
|
||||
// Extract @mentions from text → resolve to member IDs
|
||||
const mentionMatches = text.match(/@(\w+)/g);
|
||||
const mentions = mentionMatches ? [...new Set(mentionMatches.map(m => m.slice(1)))] : [];
|
||||
const mentionSlugs = mentionMatches ? [...new Set(mentionMatches.map(m => m.slice(1)))] : [];
|
||||
const mentions = mentionSlugs
|
||||
.map(slug => mentionMapRef.current.get(slug))
|
||||
.filter((id): id is string => !!id);
|
||||
|
||||
const msg = await sendMessage({
|
||||
chat_id: chatId,
|
||||
@ -422,6 +427,7 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
onSubmit={handleSend}
|
||||
onMentionInsert={(id, slug) => mentionMapRef.current.set(slug, id)}
|
||||
placeholder="Сообщение... (@mention)"
|
||||
disabled={sending}
|
||||
/>
|
||||
|
||||
@ -7,11 +7,12 @@ interface Props {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
onMentionInsert?: (memberId: string, slug: string) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function MentionInput({ projectId, value, onChange, onSubmit, placeholder, disabled }: Props) {
|
||||
export default function MentionInput({ projectId, value, onChange, onSubmit, onMentionInsert, placeholder, disabled }: Props) {
|
||||
const [members, setMembers] = useState<ProjectMember[]>([]);
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
const [filter, setFilter] = useState("");
|
||||
@ -37,6 +38,7 @@ export default function MentionInput({ projectId, value, onChange, onSubmit, pla
|
||||
const after = value.slice(inputRef.current?.selectionStart ?? value.length);
|
||||
const newValue = `${before}@${member.slug} ${after}`;
|
||||
onChange(newValue);
|
||||
onMentionInsert?.(member.id, member.slug);
|
||||
setShowDropdown(false);
|
||||
setMentionStart(-1);
|
||||
// Focus and set cursor after mention
|
||||
|
||||
@ -6,10 +6,10 @@ import { logout } from "@/lib/auth-client";
|
||||
|
||||
interface Props {
|
||||
projects: Project[];
|
||||
activeSlug?: string;
|
||||
activeId?: string;
|
||||
}
|
||||
|
||||
export default function Sidebar({ projects, activeSlug }: Props) {
|
||||
export default function Sidebar({ projects, activeId }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const nav = (
|
||||
@ -42,10 +42,10 @@ export default function Sidebar({ projects, activeSlug }: Props) {
|
||||
{projects.map((p) => (
|
||||
<Link
|
||||
key={p.id}
|
||||
to={`/projects/${p.slug}`}
|
||||
to={`/projects/${p.id}`}
|
||||
onClick={() => setOpen(false)}
|
||||
className={`block px-3 py-2 rounded text-sm transition-colors ${
|
||||
activeSlug === p.slug
|
||||
activeId === p.id
|
||||
? "bg-[var(--accent)]/10 text-[var(--accent)]"
|
||||
: "hover:bg-white/5 text-[var(--fg)]"
|
||||
}`}
|
||||
|
||||
@ -9,12 +9,12 @@ function getToken(): string | null {
|
||||
return localStorage.getItem("tb_token");
|
||||
}
|
||||
|
||||
export function getCurrentSlug(): string {
|
||||
export function getCurrentMemberId(): string {
|
||||
const token = getToken();
|
||||
if (!token) return "unknown";
|
||||
try {
|
||||
const payload = JSON.parse(atob(token.split(".")[1]));
|
||||
return payload.slug || payload.sub || "unknown";
|
||||
return payload.sub || "unknown";
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@ -27,5 +27,5 @@ export default function HomePage() {
|
||||
}
|
||||
|
||||
// Redirect to first project
|
||||
return <Navigate to={`/projects/${projects[0].slug}`} replace />;
|
||||
return <Navigate to={`/projects/${projects[0].id}`} replace />;
|
||||
}
|
||||
@ -16,7 +16,7 @@ const TABS = [
|
||||
];
|
||||
|
||||
export default function ProjectPage() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const { id: projectId } = useParams<{ id: string }>();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@ -36,7 +36,7 @@ export default function ProjectPage() {
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const project = projects.find((p) => p.slug === slug);
|
||||
const project = projects.find((p) => p.id === projectId);
|
||||
|
||||
const handleProjectUpdated = (updated: Project) => {
|
||||
setProjects((prev) => prev.map((p) => (p.id === updated.id ? updated : p)));
|
||||
@ -52,7 +52,7 @@ export default function ProjectPage() {
|
||||
|
||||
return (
|
||||
<div className="flex h-[100dvh]">
|
||||
<Sidebar projects={projects} activeSlug={slug} />
|
||||
<Sidebar projects={projects} activeId={projectId} />
|
||||
<main className="flex-1 flex flex-col overflow-hidden">
|
||||
<header className="border-b border-[var(--border)] px-6 py-3 pl-14 md:pl-6">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user