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:
riyadhafraa
2026-05-12 10:51:31 +00:00
parent e4ebaffe73
commit 84398de390
27 changed files with 22 additions and 6492 deletions
-539
View File
@@ -18,10 +18,6 @@ tags:
description: Internal services (khidmati)
- name: orders
description: Service orders workflow
- name: conversations
description: Chat conversations
- name: messages
description: Chat messages
- name: notifications
description: Notifications
- name: users
@@ -928,254 +924,6 @@ paths:
items:
$ref: "#/components/schemas/ServiceCategory"
# Conversations
/conversations:
get:
operationId: listConversations
tags: [conversations]
summary: List conversations for current user
responses:
"200":
description: Conversations
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ConversationWithDetails"
post:
operationId: createConversation
tags: [conversations]
summary: Create a conversation
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateConversationBody"
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}:
get:
operationId: getConversation
tags: [conversations]
summary: Get conversation by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
patch:
operationId: updateConversation
tags: [conversations]
summary: Update conversation properties (admin only)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateConversationBody"
responses:
"200":
description: Updated conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}/participants:
post:
operationId: addConversationParticipants
tags: [conversations]
summary: Add participants to a group conversation (admin only)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AddParticipantsBody"
responses:
"200":
description: Updated conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}/participants/{userId}:
delete:
operationId: removeConversationParticipant
tags: [conversations]
summary: Remove a participant from a group conversation (admin only)
parameters:
- name: id
in: path
required: true
schema:
type: integer
- name: userId
in: path
required: true
schema:
type: integer
responses:
"200":
description: Updated conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}/state:
patch:
operationId: updateConversationState
tags: [conversations]
summary: Update per-user state (mute, archive)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateConversationStateBody"
responses:
"200":
description: Updated conversation
content:
application/json:
schema:
$ref: "#/components/schemas/ConversationWithDetails"
/conversations/{id}/leave:
post:
operationId: leaveConversation
tags: [conversations]
summary: Leave a group conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
successorId:
type: integer
description: |
When the leaver is the only admin, optionally name a specific
remaining member to promote. Omit (or send null) to keep the
automatic behavior of promoting the longest-tenured member.
nullable: true
responses:
"200":
description: Left conversation
content:
application/json:
schema:
$ref: "#/components/schemas/SuccessResponse"
/conversations/{id}/messages:
get:
operationId: listMessages
tags: [messages]
summary: List messages in a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Messages
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/MessageWithSender"
post:
operationId: sendMessage
tags: [messages]
summary: Send a message
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendMessageBody"
responses:
"201":
description: Message sent
content:
application/json:
schema:
$ref: "#/components/schemas/MessageWithSender"
/conversations/{id}/read:
post:
operationId: markConversationRead
tags: [messages]
summary: Mark all messages in conversation as read
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Marked as read
content:
application/json:
schema:
$ref: "#/components/schemas/SuccessResponse"
# Notifications
/notifications:
get:
@@ -3074,70 +2822,6 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
/admin/users/{id}/dependents/conversations:
get:
operationId: getAdminUserDependentConversations
tags: [users]
summary: List conversations created by a user
parameters:
- in: path
name: id
required: true
schema: { type: integer }
- in: query
name: limit
required: false
schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
- in: query
name: offset
required: false
schema: { type: integer, minimum: 0, default: 0 }
responses:
"200":
description: Paged list of conversations
content:
application/json:
schema:
$ref: "#/components/schemas/UserDependentConversationsPage"
"404":
description: User not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/admin/users/{id}/dependents/messages:
get:
operationId: getAdminUserDependentMessages
tags: [users]
summary: List messages sent by a user
parameters:
- in: path
name: id
required: true
schema: { type: integer }
- in: query
name: limit
required: false
schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
- in: query
name: offset
required: false
schema: { type: integer, minimum: 0, default: 0 }
responses:
"200":
description: Paged list of messages
content:
application/json:
schema:
$ref: "#/components/schemas/UserDependentMessagesPage"
"404":
description: User not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
components:
schemas:
HealthStatus:
@@ -3463,16 +3147,6 @@ components:
description: |
Number of service orders owned by this user. Populated only by the
admin list endpoint (GET /users).
conversationCount:
type: integer
description: |
Number of conversations created by this user. Populated only by the
admin list endpoint (GET /users).
messageCount:
type: integer
description: |
Number of messages authored by this user. Populated only by the
admin list endpoint (GET /users).
required:
- id
- username
@@ -3817,16 +3491,10 @@ components:
type: integer
orderCount:
type: integer
conversationCount:
type: integer
messageCount:
type: integer
required:
- error
- noteCount
- orderCount
- conversationCount
- messageCount
AppDeletionConflict:
type: object
@@ -4304,161 +3972,6 @@ components:
- deletedIds
- failedIds
ConversationWithDetails:
type: object
properties:
id:
type: integer
nameAr:
type: ["string", "null"]
nameEn:
type: ["string", "null"]
avatarUrl:
type: ["string", "null"]
isGroup:
type: boolean
createdBy:
type: integer
participants:
type: array
items:
$ref: "#/components/schemas/ParticipantInfo"
lastMessage:
$ref: "#/components/schemas/MessageWithSender"
nullable: true
unreadCount:
type: integer
isMuted:
type: boolean
isArchived:
type: boolean
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- isGroup
- createdBy
- participants
- unreadCount
- isMuted
- isArchived
- createdAt
- updatedAt
ParticipantInfo:
type: object
properties:
id:
type: integer
username:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
avatarUrl:
type: ["string", "null"]
isAdmin:
type: boolean
required:
- id
- username
- isAdmin
CreateConversationBody:
type: object
properties:
participantIds:
type: array
items:
type: integer
nameAr:
type: ["string", "null"]
nameEn:
type: ["string", "null"]
avatarUrl:
type: ["string", "null"]
isGroup:
type: boolean
required:
- participantIds
UpdateConversationBody:
type: object
properties:
avatarUrl:
type: ["string", "null"]
nameAr:
type: ["string", "null"]
nameEn:
type: ["string", "null"]
UpdateConversationStateBody:
type: object
properties:
isMuted:
type: boolean
isArchived:
type: boolean
AddParticipantsBody:
type: object
properties:
userIds:
type: array
items:
type: integer
required:
- userIds
MessageWithSender:
type: object
properties:
id:
type: integer
conversationId:
type: integer
senderId:
type: integer
content:
type: string
kind:
type: string
enum: [user, group_renamed, members_added, member_removed, member_left, admin_promoted]
meta:
type: object
nullable: true
additionalProperties: true
sender:
$ref: "#/components/schemas/ParticipantInfo"
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
required:
- id
- conversationId
- senderId
- content
- kind
- sender
- createdAt
- updatedAt
SendMessageBody:
type: object
properties:
content:
type: string
required:
- content
Notification:
type: object
properties:
@@ -4579,15 +4092,12 @@ components:
type: integer
unreadNotifications:
type: integer
unreadMessages:
type: integer
totalUsers:
type: integer
required:
- totalApps
- totalServices
- unreadNotifications
- unreadMessages
- totalUsers
AdminStats:
@@ -5179,55 +4689,6 @@ components:
nextOffset: { type: ["integer", "null"] }
required: [items, totalCount, limit, offset, nextOffset]
UserDependentConversationItem:
type: object
properties:
id: { type: integer }
nameAr: { type: ["string", "null"] }
nameEn: { type: ["string", "null"] }
isGroup: { type: boolean }
createdAt: { type: string, format: date-time }
required: [id, nameAr, nameEn, isGroup, createdAt]
UserDependentConversationsPage:
type: object
properties:
items:
type: array
items:
$ref: "#/components/schemas/UserDependentConversationItem"
totalCount: { type: integer }
limit: { type: integer }
offset: { type: integer }
nextOffset: { type: ["integer", "null"] }
required: [items, totalCount, limit, offset, nextOffset]
UserDependentMessageItem:
type: object
properties:
id: { type: integer }
content: { type: string }
kind: { type: string }
createdAt: { type: string, format: date-time }
conversationId: { type: integer }
conversationNameAr: { type: ["string", "null"] }
conversationNameEn: { type: ["string", "null"] }
conversationIsGroup: { type: boolean }
required: [id, content, kind, createdAt, conversationId, conversationNameAr, conversationNameEn, conversationIsGroup]
UserDependentMessagesPage:
type: object
properties:
items:
type: array
items:
$ref: "#/components/schemas/UserDependentMessageItem"
totalCount: { type: integer }
limit: { type: integer }
offset: { type: integer }
nextOffset: { type: ["integer", "null"] }
required: [items, totalCount, limit, offset, nextOffset]
TopUserItem:
type: object
properties: