* Add project versioning & publish flow Introduce project model versioning and publishing support. - DB: add published_model_version column to projects (migration + schema + types). - Models: add version status types and helpers, implement getProjectVersionStatus, enhanced getProjectModel to load draft/published/legacy fallbacks, saveProjectModel now manages draft creation/updating and avoids no-op saves, saveProjectVersion to lock/save/publish versions and create next draft, publishProjectModel to republish specific saved versions. Includes scene-graph equality checks and authenticated project ownership checks. - UI: AppSidebar shows publish/draft status, polling refresh, and Save / Save & publish / Publish actions that flush the editor scene before version operations. Hook update: load scene from result.data.model.scene_graph. - Public project loader: prefer published version with legacy fallbacks. This enables safe draft editing, explicit saves, publishing, and keeps autosave working across version locks. * Add project versioning UI, APIs & preview mode Introduce full version management support: UI, backend actions, and editor integration. - UI: Add a Versions popover to the app sidebar with search, relative timestamps, preview, restore and publish controls; disable version actions while previewing and show proper labels/states. Imported new icons, popover and tooltip primitives and added formatRelativeTime helper. - Actions: Add ProjectVersionListItem type and APIs getProjectVersionList and getProjectVersionByNumber to list and fetch saved (non-draft) versions. - Hooks: Extract applySceneGraphToEditor and sync editor selection logic; ensure scene loading applies selection and supports preview mode (which suppresses autosave). - Store: Add isVersionPreviewMode to project store with setter and isSceneLoading flag usage to prevent autosave while previewing. - Misc: Wire up version list loading, previewing, restoring and publishing flows, and refresh version status/list after actions. These changes enable browsing historical project versions, previewing them without triggering autosaves, and restoring or publishing selected versions. * Improve version preview, restore flow & draft handling - Add scene graph snapshot to preserve unsaved work during preview - Include draft versions and metadata in version list - Add getProjectVersionById for direct ID-based lookups - Add autosave status to project store - Refactor sidebar version UI (remove search, use save icon) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
import { relations } from 'drizzle-orm'
|
|
import { index, pgTable } from 'drizzle-orm/pg-core'
|
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
|
import { id, timestampsColumns } from '../../helpers'
|
|
import { users } from '../auth/users'
|
|
import { addresses } from './addresses'
|
|
|
|
export const projects = pgTable(
|
|
'projects',
|
|
(t) => ({
|
|
id: id('project'),
|
|
name: t.text('name'),
|
|
addressId: t
|
|
.text('address_id')
|
|
.references(() => addresses.id, { onDelete: 'set null' }),
|
|
ownerId: t
|
|
.text('owner_id')
|
|
.references(() => users.id, { onDelete: 'set null' }),
|
|
detailsJson: t.jsonb('details_json'),
|
|
metadata: t.jsonb('metadata'),
|
|
publishedModelVersion: t.integer('published_model_version'),
|
|
// Community features
|
|
isPrivate: t.boolean('is_private').notNull().default(true),
|
|
isEmpty: t.boolean('is_empty').notNull().default(true),
|
|
showScansPublic: t.boolean('show_scans_public').notNull().default(true),
|
|
showGuidesPublic: t.boolean('show_guides_public').notNull().default(true),
|
|
views: t.integer('views').notNull().default(0),
|
|
likes: t.integer('likes').notNull().default(0),
|
|
thumbnailUrl: t.text('thumbnail_url'),
|
|
...timestampsColumns,
|
|
}),
|
|
(t) => [
|
|
index('project_address_idx').on(t.addressId),
|
|
index('project_owner_idx').on(t.ownerId),
|
|
index('project_is_private_idx').on(t.isPrivate),
|
|
index('project_views_idx').on(t.views),
|
|
index('project_likes_idx').on(t.likes),
|
|
],
|
|
).enableRLS()
|
|
|
|
export const projectsRelations = relations(projects, ({ one }) => ({
|
|
address: one(addresses, {
|
|
fields: [projects.addressId],
|
|
references: [addresses.id],
|
|
}),
|
|
owner: one(users, {
|
|
fields: [projects.ownerId],
|
|
references: [users.id],
|
|
}),
|
|
}))
|
|
|
|
export type Project = typeof projects.$inferSelect
|
|
export type NewProject = typeof projects.$inferInsert
|
|
export const insertProjectSchema = createInsertSchema(projects)
|
|
export const selectProjectSchema = createSelectSchema(projects)
|