Task #511: Fully remove Chat feature
Destructive removal per user confirmation ("حذف نهائي ما يرجع").
Removed:
- API: routes/conversations.ts, schema/conversations.ts, all chat
socket handlers in src/index.ts, /admin/users/:id/dependents/
conversations+messages endpoints, conversation/message dependency
counts in users/stats routes.
- Web: pages/chat.tsx, /chat route, dock chat filter, MessageSquare
icon and messages StatCard on home, all chat-related UI in
notifications + admin (dependency badges, delete-dialog rows,
UserDependentConversations/Messages sections, count map keys).
- Locales: nav.chat, home.stats.messages, full chat.* block,
admin.deleteUser conv/msgCount, admin.users.counts.conv/msg,
admin.audit.unit.conversation_*/message_*, admin.dependents.user*.
- OpenAPI spec: tags, all /conversations/* paths, conv/msg dependent
paths, related schemas (ConversationWithDetails, MessageWithSender,
UserDependentConversation/MessageItem+Page, etc.), UserProfile and
UserDeletionConflict conv/msg fields, HomeStats.unreadMessages.
Regenerated client via orval.
- Database: dropped message_reads, messages,
conversation_participants, conversations (CASCADE); deleted
notifications with related_type='conversation' or type='chat';
deleted apps row with slug='chat'; ran drizzle push-force.
- Seed: removed chat:access permission + user-role assignment +
seeded chat app entry from scripts/src/seed.ts.
- Tests: deleted conversations-leave.test.mjs; cleaned chat refs from
list-dependency-counts, delete-force-warnings, audit-log-coverage,
and admin-inline-dependency-counts (e2e) — replaced chat dependents
with note dependents where needed for force-delete coverage.
Notes preserved: notes.tsx noConversationsYet/conversationWith refer
to NOTE THREADS (not chat) and were intentionally NOT touched.
executive-meetings.ts not modified per replit.md restriction.
Pre-existing flaky test failures in executive-meetings/group/etc
suites remain unrelated to this task.
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
import { pgTable, text, serial, timestamp, integer, varchar, boolean, primaryKey, jsonb } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod/v4";
|
||||
import { usersTable } from "./users";
|
||||
|
||||
export const conversationsTable = pgTable("conversations", {
|
||||
id: serial("id").primaryKey(),
|
||||
nameAr: varchar("name_ar", { length: 300 }),
|
||||
nameEn: varchar("name_en", { length: 300 }),
|
||||
avatarUrl: text("avatar_url"),
|
||||
isGroup: boolean("is_group").notNull().default(false),
|
||||
createdBy: integer("created_by").notNull().references(() => usersTable.id),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
export const conversationParticipantsTable = pgTable("conversation_participants", {
|
||||
conversationId: integer("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
|
||||
userId: integer("user_id").notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
joinedAt: timestamp("joined_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
isAdmin: boolean("is_admin").notNull().default(false),
|
||||
isMuted: boolean("is_muted").notNull().default(false),
|
||||
isArchived: boolean("is_archived").notNull().default(false),
|
||||
}, (t) => [primaryKey({ columns: [t.conversationId, t.userId] })]);
|
||||
|
||||
export const messagesTable = pgTable("messages", {
|
||||
id: serial("id").primaryKey(),
|
||||
conversationId: integer("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
|
||||
senderId: integer("sender_id").notNull().references(() => usersTable.id),
|
||||
content: text("content").notNull(),
|
||||
kind: varchar("kind", { length: 32 }).notNull().default("user"),
|
||||
meta: jsonb("meta"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
export const messageReadsTable = pgTable("message_reads", {
|
||||
messageId: integer("message_id").notNull().references(() => messagesTable.id, { onDelete: "cascade" }),
|
||||
userId: integer("user_id").notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
readAt: timestamp("read_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (t) => [primaryKey({ columns: [t.messageId, t.userId] })]);
|
||||
|
||||
export const insertMessageSchema = createInsertSchema(messagesTable).omit({ id: true, createdAt: true, updatedAt: true });
|
||||
export const insertConversationSchema = createInsertSchema(conversationsTable).omit({ id: true, createdAt: true, updatedAt: true });
|
||||
export type InsertMessage = z.infer<typeof insertMessageSchema>;
|
||||
export type Message = typeof messagesTable.$inferSelect;
|
||||
export type Conversation = typeof conversationsTable.$inferSelect;
|
||||
@@ -3,7 +3,6 @@ export * from "./roles";
|
||||
export * from "./apps";
|
||||
export * from "./services";
|
||||
export * from "./service-orders";
|
||||
export * from "./conversations";
|
||||
export * from "./notifications";
|
||||
export * from "./settings";
|
||||
export * from "./user-app-orders";
|
||||
|
||||
Reference in New Issue
Block a user