mirror of
https://github.com/d0zingcat/alert-message-center.git
synced 2026-05-13 15:09:19 +00:00
Merge pull request #1 from d0zingcat/enhancement/topic_display
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
export { };
|
||||
|
||||
// Simulate admin checking requests
|
||||
async function run() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { };
|
||||
|
||||
async function run() {
|
||||
console.log('Fetching dashboard stats as admin...');
|
||||
|
||||
@@ -32,6 +32,8 @@ api.get('/topics', requireAuth, async (c) => {
|
||||
const allTopics = await db.query.topics.findMany({
|
||||
where: eq(topics.status, 'approved'),
|
||||
with: {
|
||||
creator: true,
|
||||
approver: true,
|
||||
subscriptions: {
|
||||
where: (subscriptions, { eq }) =>
|
||||
isAdmin ? undefined : (currentUserId ? eq(subscriptions.userId, currentUserId) : undefined),
|
||||
@@ -59,6 +61,7 @@ api.get('/topics/all', requireAdmin, async (c) => {
|
||||
const allTopics = await db.query.topics.findMany({
|
||||
with: {
|
||||
creator: true,
|
||||
approver: true,
|
||||
subscriptions: true
|
||||
},
|
||||
orderBy: [desc(topics.createdAt)]
|
||||
@@ -71,14 +74,18 @@ api.get('/topics/my-requests', requireAuth, async (c) => {
|
||||
const requests = await db.query.topics.findMany({
|
||||
where: eq(topics.createdBy, session.id),
|
||||
orderBy: [desc(topics.createdAt)],
|
||||
with: {
|
||||
approver: true,
|
||||
}
|
||||
});
|
||||
return c.json(requests);
|
||||
});
|
||||
|
||||
api.post('/topics/:id/approve', requireAdmin, async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const session = c.get('session');
|
||||
const result = await db.update(topics)
|
||||
.set({ status: 'approved' })
|
||||
.set({ status: 'approved', approvedBy: session.id })
|
||||
.where(eq(topics.id, id))
|
||||
.returning();
|
||||
return c.json(result[0]);
|
||||
@@ -105,6 +112,7 @@ api.post('/topics', requireAuth, zValidator('json', topicSchema), async (c) => {
|
||||
...body,
|
||||
status,
|
||||
createdBy: session.id,
|
||||
approvedBy: session.isAdmin ? session.id : null,
|
||||
}).returning();
|
||||
return c.json(result[0]);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ export const topics = pgTable('topics', {
|
||||
description: text('description'),
|
||||
status: text('status', { enum: ['pending', 'approved', 'rejected'] }).default('approved').notNull(),
|
||||
createdBy: text('created_by').references(() => users.id),
|
||||
approvedBy: text('approved_by').references(() => users.id),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
@@ -17,6 +18,12 @@ export const topicsRelations = relations(topics, ({ many, one }) => ({
|
||||
creator: one(users, {
|
||||
fields: [topics.createdBy],
|
||||
references: [users.id],
|
||||
relationName: 'creator',
|
||||
}),
|
||||
approver: one(users, {
|
||||
fields: [topics.approvedBy],
|
||||
references: [users.id],
|
||||
relationName: 'approver',
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -31,6 +38,8 @@ export const users = pgTable('users', {
|
||||
|
||||
export const usersRelations = relations(users, ({ many }) => ({
|
||||
subscriptions: many(subscriptions),
|
||||
createdTopics: many(topics, { relationName: 'creator' }),
|
||||
approvedTopics: many(topics, { relationName: 'approver' }),
|
||||
}));
|
||||
|
||||
// Subscriptions: 用户直接订阅 Topic
|
||||
|
||||
@@ -99,6 +99,7 @@ function TopicsManagement() {
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Subscribers</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created By</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Approved By</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -123,6 +124,9 @@ function TopicsManagement() {
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{topic.creator?.name || 'Unknown'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{topic.approver?.name || '-'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
onClick={() => handleDelete(topic.id, topic.name)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Plus, Settings, UserPlus, UserMinus, Copy, Check } from 'lucide-react';
|
||||
import { Plus, Settings, UserPlus, UserMinus, Copy, Check, User, ShieldCheck } from 'lucide-react';
|
||||
import Modal from '../components/Modal';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { client } from '../lib/client';
|
||||
@@ -21,6 +21,8 @@ interface Topic {
|
||||
slug: string;
|
||||
description?: string;
|
||||
subscriptions: Subscription[];
|
||||
creator?: User;
|
||||
approver?: User;
|
||||
}
|
||||
|
||||
export default function TopicsView() {
|
||||
@@ -281,6 +283,20 @@ export default function TopicsView() {
|
||||
<p className="flex items-center text-sm text-gray-500 mt-1">
|
||||
{topic.description}
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 mt-2">
|
||||
{topic.creator && (
|
||||
<div className="flex items-center text-xs text-gray-500">
|
||||
<User className="w-3 h-3 mr-1 text-gray-400" />
|
||||
<span>Created by: <span className="text-gray-900 font-medium">{topic.creator.name}</span></span>
|
||||
</div>
|
||||
)}
|
||||
{topic.approver && (
|
||||
<div className="flex items-center text-xs text-gray-500">
|
||||
<ShieldCheck className="w-3 h-3 mr-1 text-indigo-400" />
|
||||
<span>Approved by: <span className="text-gray-900 font-medium">{topic.approver.name}</span></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{currentUser && (
|
||||
<div className="mt-3 bg-gray-50 p-2 rounded border border-gray-200">
|
||||
<div className="flex justify-between items-center">
|
||||
@@ -358,7 +374,10 @@ export default function TopicsView() {
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
<p>Slug: <span className="font-mono">{req.slug}</span></p>
|
||||
{req.description && <p className="mt-1">{req.description}</p>}
|
||||
<p className="mt-1 text-xs text-gray-400">Requested on: {new Date(req.createdAt).toLocaleDateString()}</p>
|
||||
<p className="mt-1 text-xs text-gray-400">
|
||||
Requested on: {new Date(req.createdAt).toLocaleDateString()}
|
||||
{req.approver && <span className="ml-2">| Approved by: {req.approver.name}</span>}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user