feat: rename properties to projects

- Rename all DB tables: properties → projects, properties_addresses → projects_addresses, properties_models → projects_models, property_likes → projects_likes
- Add projects_likes as proper Drizzle schema table (was SQL-only before)
- Rename ID prefixes: property_xxx → project_xxx
- Rename auth_sessions column: active_property_id → active_project_id
- Simplify project creation: address is now optional (no longer required to get started)
- Update all actions, stores, types, components, and routes
- Rename route: /editor/[propertyId] → /editor/[projectId]
- Add SQL migration for table/column/index/policy/function renames
- Update RPC functions: increment_project_views, get_project_like_count
- Update storage bucket reference: project-thumbnails
- All types/exports follow existing Drizzle patterns (pgTable, relations, zod schemas)
This commit is contained in:
Anton Pascal
2026-02-17 20:40:52 +00:00
parent 77d64824e5
commit ee30f17129
35 changed files with 1376 additions and 1025 deletions
@@ -0,0 +1,53 @@
import { pgTable, unique } from 'drizzle-orm/pg-core'
import { z } from 'zod'
import { id, timestampsColumns } from '../../helpers'
export const addresses = pgTable(
'projects_addresses',
(t) => ({
id: id('address'),
streetNumber: t.text('street_number'),
route: t.text('route'),
routeShort: t.text('route_short'),
neighborhood: t.text('neighborhood'),
city: t.text('city'),
county: t.text('county'),
state: t.text('state'),
stateLong: t.text('state_long'),
postalCode: t.text('postal_code'),
postalCodeSuffix: t.text('postal_code_suffix'),
country: t.text('country'),
countryLong: t.text('country_long'),
latitude: t.numeric('latitude'),
longitude: t.numeric('longitude'),
rawJson: t.jsonb('raw_json'),
...timestampsColumns,
}),
(t) => [
// Unique constraint on core address components to prevent duplicates
unique('address_components_unique').on(t.streetNumber, t.route, t.city, t.state, t.postalCode),
],
).enableRLS()
// Create address schema manually to avoid issues with generated columns
export const addressSchema = z.object({
streetNumber: z.string().optional(),
route: z.string().optional(),
routeShort: z.string().optional(),
neighborhood: z.string().optional(),
city: z.string().optional(),
county: z.string().optional(),
state: z.string().optional(),
stateLong: z.string().optional(),
postalCode: z.string().optional(),
postalCodeSuffix: z.string().optional(),
country: z.string().default('US'),
countryLong: z.string().optional(),
latitude: z.string().optional(),
longitude: z.string().optional(),
rawJson: z.record(z.string(), z.unknown()).optional(),
})
export type AddressSchema = z.infer<typeof addressSchema>
export type Address = typeof addresses.$inferSelect
export type NewAddress = typeof addresses.$inferInsert
+36
View File
@@ -0,0 +1,36 @@
import { relations } from 'drizzle-orm'
import { pgTable, unique } from 'drizzle-orm/pg-core'
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
import { id, createdAt } from '../../helpers'
import { projects } from './projects'
import { users } from '../auth/users'
export const projectsLikes = pgTable(
'projects_likes',
(t) => ({
id: id('like'),
projectId: t
.text('project_id')
.notNull()
.references(() => projects.id, { onDelete: 'cascade' }),
userId: t
.text('user_id')
.notNull(),
createdAt,
}),
(t) => [
unique('projects_likes_project_user_unique').on(t.projectId, t.userId),
],
).enableRLS()
export const projectsLikesRelations = relations(projectsLikes, ({ one }) => ({
project: one(projects, {
fields: [projectsLikes.projectId],
references: [projects.id],
}),
}))
export type ProjectLike = typeof projectsLikes.$inferSelect
export type NewProjectLike = typeof projectsLikes.$inferInsert
export const insertProjectLikeSchema = createInsertSchema(projectsLikes)
export const selectProjectLikeSchema = createSelectSchema(projectsLikes)
+31
View File
@@ -0,0 +1,31 @@
import { relations } from 'drizzle-orm'
import { pgTable } from 'drizzle-orm/pg-core'
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
import { id, timestampsColumnsSoftDelete } from '../../helpers'
import { projects } from './projects'
export const models = pgTable('projects_models', (t) => ({
id: id('model'),
name: t.text('name'),
version: t.integer('version').default(1),
description: t.text('description'),
draft: t.boolean('draft').default(true),
projectId: t
.text('project_id')
.references(() => projects.id, { onDelete: 'set null' }),
sceneGraph: t.jsonb('scene_graph'),
metadata: t.jsonb('metadata'),
...timestampsColumnsSoftDelete,
})).enableRLS()
export const modelsRelations = relations(models, ({ one }) => ({
project: one(projects, {
fields: [models.projectId],
references: [projects.id],
}),
}))
export type Model = typeof models.$inferSelect
export type NewModel = typeof models.$inferInsert
export const insertModelSchema = createInsertSchema(models)
export const selectModelSchema = createSelectSchema(models)
@@ -0,0 +1,51 @@
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'),
// Community features
isPrivate: t.boolean('is_private').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)