Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/app/routers/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ async def get_page(slug: str, ctx: PageAccess = Depends(page_dep("read"))):
user = ctx.user
permission = ctx.permission
rows = await db.execute_fetchall(
"""SELECT p.*, CASE WHEN u.display_name IS NOT NULL AND u.display_name != '' THEN u.display_name ELSE u.username END AS author_name
"""SELECT p.*, u.username AS author_username,
CASE WHEN u.display_name IS NOT NULL AND u.display_name != '' THEN u.display_name ELSE u.username END AS author_name
FROM pages p
LEFT JOIN users u ON u.id = p.created_by
WHERE p.id = ?""",
Expand Down
1 change: 1 addition & 0 deletions backend/app/routers/trash.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ async def list_trash(user=Depends(get_current_user)):
base_sql = """
SELECT p.id, p.slug, p.title, p.content_md, p.parent_id, p.version,
p.view_count, p.created_by, p.deleted_at, p.updated_at,
u.username AS author_username,
CASE WHEN u.display_name IS NOT NULL AND u.display_name != ''
THEN u.display_name ELSE u.username END AS author_name
FROM pages p
Expand Down
1 change: 1 addition & 0 deletions backend/app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class PageResponse(BaseModel):
mindmap_layout: MindmapLayout | None = None
created_by: int | None = None
author_name: str | None = None
author_username: str | None = None
created_at: str | None = None
updated_at: str | None = None
effective_permission: str | None = None
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/components/Comments.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import DOMPurify from 'dompurify'
import useAuth from '../store/useAuth'
Expand Down Expand Up @@ -26,7 +27,16 @@ function CommentItem({ comment, currentUser, onDelete, onUpdate, pageSlug }) {
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="text-sm font-medium text-text">{comment.display_name || comment.username}</span>
{comment.username ? (
<Link
to={`/u/${encodeURIComponent(comment.username)}`}
className="text-sm font-medium text-text hover:underline"
>
{comment.display_name || comment.username}
</Link>
) : (
<span className="text-sm font-medium text-text">{comment.display_name || '?'}</span>
)}
<span className="text-xs text-text-secondary">{new Date(comment.created_at).toLocaleString()}</span>
{comment.updated_at !== comment.created_at && (
<span className="text-xs text-text-secondary">{t('comments.edited')}</span>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@
"title": "Account Info",
"username": "Username",
"role": "Role",
"created": "Created"
"created": "Created",
"viewPublic": "View public profile"
},
"edit": {
"title": "Edit Profile",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@
"title": "アカウント情報",
"username": "ユーザー名",
"role": "ロール",
"created": "作成日"
"created": "作成日",
"viewPublic": "公開プロフィールを見る"
},
"edit": {
"title": "プロフィールを編集",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@
"title": "계정 정보",
"username": "사용자명",
"role": "역할",
"created": "생성일"
"created": "생성일",
"viewPublic": "공개 프로필 보기"
},
"edit": {
"title": "프로필 편집",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@
"title": "帳號資訊",
"username": "使用者名稱",
"role": "角色",
"created": "建立時間"
"created": "建立時間",
"viewPublic": "檢視公開頁面"
},
"edit": {
"title": "編輯個人檔案",
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/Activity.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
useEffect(() => {
fetchActivity()
fetchStats(true)
}, [])

Check warning on line 19 in frontend/src/pages/Activity.jsx

View workflow job for this annotation

GitHub Actions / frontend-tests

React Hook useEffect has missing dependencies: 'fetchActivity' and 'fetchStats'. Either include them or remove the dependency array

const actionLabel = (action) => {
const key = `activity.actions.${action}`
Expand Down Expand Up @@ -89,7 +89,16 @@
{text}
</span>
<div className="flex-1 min-w-0">
<span className="text-sm font-medium text-text">{a.display_name || a.username || t('activity.system')}</span>
{a.username ? (
<Link
to={`/u/${encodeURIComponent(a.username)}`}
className="text-sm font-medium text-text hover:underline"
>
{a.display_name || a.username}
</Link>
) : (
<span className="text-sm font-medium text-text">{t('activity.system')}</span>
)}
<span className="text-sm text-text-secondary">
{' '}{text}{' '}
{meta.slug && a.action !== 'deleted' ? (
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/pages/Admin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,14 @@
<tbody>
{users.map((u) => (
<tr key={u.id} className="border-b border-border">
<td className="py-2 px-3 text-text">{u.username}</td>
<td className="py-2 px-3 text-text">
<Link
to={`/u/${encodeURIComponent(u.username)}`}
className="hover:underline"
>
{u.username}
</Link>
</td>
<td className="py-2 px-3">
<select
value={u.role}
Expand Down Expand Up @@ -546,7 +553,7 @@

useEffect(() => {
fetchGroups()
}, [])

Check warning on line 556 in frontend/src/pages/Admin.jsx

View workflow job for this annotation

GitHub Actions / frontend-tests

React Hook useEffect has a missing dependency: 'fetchGroups'. Either include it or remove the dependency array

const handleCreate = async (e) => {
e.preventDefault()
Expand Down Expand Up @@ -768,7 +775,7 @@

useEffect(() => {
loadMedia()
}, [])

Check warning on line 778 in frontend/src/pages/Admin.jsx

View workflow job for this annotation

GitHub Actions / frontend-tests

React Hook useEffect has a missing dependency: 'loadMedia'. Either include it or remove the dependency array

const handleDelete = async (item) => {
if (item.reference_count > 0) return
Expand Down Expand Up @@ -953,7 +960,7 @@

useEffect(() => {
loadDiagrams()
}, [])

Check warning on line 963 in frontend/src/pages/Admin.jsx

View workflow job for this annotation

GitHub Actions / frontend-tests

React Hook useEffect has a missing dependency: 'loadDiagrams'. Either include it or remove the dependency array

const handleDelete = async (item) => {
if (item.reference_count > 0) return
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/pages/PageVersions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,19 @@ export default function PageVersions() {
</button>
</div>
<div className="text-xs text-text-secondary mt-1">
{v.display_name || v.username || t('versions.unknownAuthor')} &middot; {new Date(v.edited_at).toLocaleString()}
{v.username ? (
<Link
to={`/u/${encodeURIComponent(v.username)}`}
onClick={(e) => e.stopPropagation()}
className="hover:text-text hover:underline"
>
{v.display_name || v.username}
</Link>
) : (
t('versions.unknownAuthor')
)}
{' '}&middot;{' '}
{new Date(v.edited_at).toLocaleString()}
</div>
<div className="text-xs text-text-secondary mt-0.5 truncate">{v.title}</div>
</div>
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/pages/PageView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,18 @@ export default function PageView() {
</div>

<div className="text-sm text-text-secondary mb-6">
{page.author_name && <>{page.author_name} &middot; </>}
{page.author_name && (
<>
{page.author_username ? (
<Link to={`/u/${encodeURIComponent(page.author_username)}`} className="hover:text-text hover:underline">
{page.author_name}
</Link>
) : (
page.author_name
)}
{' '}&middot;{' '}
</>
)}
/{page.slug} &middot; {t('common.views', { count: page.view_count })} &middot; {t('pageView.updated', { date: new Date(page.updated_at).toLocaleString() })}
{page.is_public && <> &middot; <span title={t('pageView.publicTitle')}>{t('pageView.publicLabel')}</span></>}
{watcherCount > 0 && <> &middot; {t('pageView.watchingCount', { count: watcherCount })}</>}
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/Profile.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import useAuth from '../store/useAuth'
import api from '../api/client'
Expand Down Expand Up @@ -91,7 +92,15 @@ export default function Profile() {

{/* Account Info */}
<div className="bg-surface rounded-xl shadow-sm border border-border p-6">
<h2 className="text-lg font-semibold text-text mb-4">{t('profile.account.title')}</h2>
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-text">{t('profile.account.title')}</h2>
<Link
to={`/u/${encodeURIComponent(profile.username)}`}
className="text-sm text-primary hover:underline"
>
{t('profile.account.viewPublic')}
</Link>
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-text-secondary">{t('profile.account.username')}</span>
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/pages/Trash.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import { Link, useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import api from '../api/client'
import useAuth from '../store/useAuth'
Expand Down Expand Up @@ -105,7 +105,18 @@ export default function Trash() {
<div className="text-xs text-text-secondary">/{item.slug}</div>
</td>
<td className="px-4 py-3 text-text-secondary">
{item.author_name || '—'}
{item.author_name ? (
item.author_username ? (
<Link
to={`/u/${encodeURIComponent(item.author_username)}`}
className="hover:text-text hover:underline"
>
{item.author_name}
</Link>
) : (
item.author_name
)
) : '—'}
</td>
<td className="px-4 py-3 text-text-secondary">
{item.deleted_at ? new Date(item.deleted_at).toLocaleString() : '—'}
Expand Down
Loading