Merge public-gh/master into paperclip-company-import-export

This commit is contained in:
Dotta
2026-03-16 07:38:08 -05:00
28 changed files with 863 additions and 37 deletions

View File

@@ -75,11 +75,15 @@ export function CommandPalette() {
enabled: !!selectedCompanyId && open,
});
const { data: projects = [] } = useQuery({
const { data: allProjects = [] } = useQuery({
queryKey: queryKeys.projects.list(selectedCompanyId!),
queryFn: () => projectsApi.list(selectedCompanyId!),
enabled: !!selectedCompanyId && open,
});
const projects = useMemo(
() => allProjects.filter((p) => !p.archivedAt),
[allProjects],
);
function go(path: string) {
setOpen(false);

View File

@@ -131,8 +131,12 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp
queryFn: () => projectsApi.list(companyId!),
enabled: !!companyId,
});
const activeProjects = useMemo(
() => (projects ?? []).filter((p) => !p.archivedAt || p.id === issue.projectId),
[projects, issue.projectId],
);
const { orderedProjects } = useProjectOrder({
projects: projects ?? [],
projects: activeProjects,
companyId,
userId: currentUserId,
});

View File

@@ -117,7 +117,7 @@ export function MarkdownBody({ children, className }: MarkdownBodyProps) {
return (
<div
className={cn(
"paperclip-markdown prose prose-sm max-w-none break-words overflow-hidden prose-pre:whitespace-pre-wrap prose-pre:break-words prose-code:break-all",
"paperclip-markdown prose prose-sm max-w-none break-words overflow-hidden",
theme === "dark" && "prose-invert",
className,
)}

View File

@@ -288,8 +288,12 @@ export function NewIssueDialog() {
queryFn: () => authApi.getSession(),
});
const currentUserId = session?.user?.id ?? session?.session?.userId ?? null;
const activeProjects = useMemo(
() => (projects ?? []).filter((p) => !p.archivedAt),
[projects],
);
const { orderedProjects } = useProjectOrder({
projects: projects ?? [],
projects: activeProjects,
companyId: effectiveCompanyId,
userId: currentUserId,
});

View File

@@ -13,7 +13,7 @@ import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { AlertCircle, Check, ExternalLink, Github, Loader2, Plus, Trash2, X } from "lucide-react";
import { AlertCircle, Archive, ArchiveRestore, Check, ExternalLink, Github, Loader2, Plus, Trash2, X } from "lucide-react";
import { ChoosePathButton } from "./PathInstructionsModal";
import { DraftInput } from "./agent-config-primitives";
import { InlineEditor } from "./InlineEditor";
@@ -34,6 +34,8 @@ interface ProjectPropertiesProps {
onUpdate?: (data: Record<string, unknown>) => void;
onFieldUpdate?: (field: ProjectConfigFieldKey, data: Record<string, unknown>) => void;
getFieldSaveState?: (field: ProjectConfigFieldKey) => ProjectFieldSaveState;
onArchive?: (archived: boolean) => void;
archivePending?: boolean;
}
export type ProjectFieldSaveState = "idle" | "saving" | "saved" | "error";
@@ -152,7 +154,7 @@ function ProjectStatusPicker({ status, onChange }: { status: string; onChange: (
);
}
export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSaveState }: ProjectPropertiesProps) {
export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSaveState, onArchive, archivePending }: ProjectPropertiesProps) {
const { selectedCompanyId } = useCompany();
const queryClient = useQueryClient();
const [goalOpen, setGoalOpen] = useState(false);
@@ -954,6 +956,45 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
)}
</div>
{onArchive && (
<>
<Separator className="my-4" />
<div className="space-y-4 py-4">
<div className="text-xs font-medium text-destructive uppercase tracking-wide">
Danger Zone
</div>
<div className="space-y-3 rounded-md border border-destructive/40 bg-destructive/5 px-4 py-4">
<p className="text-sm text-muted-foreground">
{project.archivedAt
? "Unarchive this project to restore it in the sidebar and project selectors."
: "Archive this project to hide it from the sidebar and project selectors."}
</p>
<Button
size="sm"
variant="destructive"
disabled={archivePending}
onClick={() => {
const action = project.archivedAt ? "Unarchive" : "Archive";
const confirmed = window.confirm(
`${action} project "${project.name}"?`,
);
if (!confirmed) return;
onArchive(!project.archivedAt);
}}
>
{archivePending ? (
<><Loader2 className="h-3 w-3 animate-spin mr-1" />{project.archivedAt ? "Unarchiving..." : "Archiving..."}</>
) : project.archivedAt ? (
<><ArchiveRestore className="h-3 w-3 mr-1" />Unarchive project</>
) : (
<><Archive className="h-3 w-3 mr-1" />Archive project</>
)}
</Button>
</div>
</div>
</>
)}
</div>
);
}