update docs
This commit is contained in:
161
web-server/views/document-detail.ejs
Normal file
161
web-server/views/document-detail.ejs
Normal file
@@ -0,0 +1,161 @@
|
||||
<%- include('partials/page-start') %>
|
||||
|
||||
<section class="page document-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<div class="breadcrumb"><a href="/documents">Tài liệu</a><span>/</span><span><%= helpers.documentCategoryLabel(document.category) %></span></div>
|
||||
<h1><%= document.title %></h1>
|
||||
<p><%= document.summary || 'Tài liệu nội bộ Robot Installer.' %></p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<% if (document.filePath) { %>
|
||||
<a class="btn btn-secondary" href="/documents/<%= document.id %>/file?download=1">
|
||||
<span class="material-symbols-outlined">download</span>
|
||||
Tải file
|
||||
</a>
|
||||
<% } %>
|
||||
<button class="btn btn-primary" type="button" data-modal-open="editDocumentModal">
|
||||
<span class="material-symbols-outlined">edit</span>
|
||||
Chỉnh sửa
|
||||
</button>
|
||||
<form method="post" action="/documents/<%= document.id %>/delete" data-confirm-submit="Xóa tài liệu <%= document.title %> và file đính kèm?">
|
||||
<button class="btn btn-danger" type="submit">
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
Xóa
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-detail-grid">
|
||||
<section class="panel document-meta-panel">
|
||||
<div class="panel-header">
|
||||
<div>
|
||||
<h2>Thông tin tài liệu</h2>
|
||||
<p>Thông tin phân loại và tệp lưu trữ.</p>
|
||||
</div>
|
||||
</div>
|
||||
<dl class="detail-list">
|
||||
<div><dt>Nhóm</dt><dd><span class="badge badge-info"><%= helpers.documentCategoryLabel(document.category) %></span></dd></div>
|
||||
<div><dt>Người tạo</dt><dd><%= document.createdBy %></dd></div>
|
||||
<div><dt>Ngày tạo</dt><dd><%= document.createdAt %></dd></div>
|
||||
<div><dt>Cập nhật</dt><dd><%= document.updatedAt %></dd></div>
|
||||
<div><dt>File</dt><dd><%= document.originalFileName || 'Không có' %></dd></div>
|
||||
<div><dt>Dung lượng</dt><dd><%= document.fileSize || '-' %></dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="panel document-reader-panel">
|
||||
<div class="panel-header">
|
||||
<div>
|
||||
<h2>Nội dung</h2>
|
||||
<p>Đọc nội dung và xem trước file được trình duyệt hỗ trợ.</p>
|
||||
</div>
|
||||
<% if (document.filePath) { %>
|
||||
<a class="text-link" href="/documents/<%= document.id %>/file" target="_blank" rel="noopener">Mở file</a>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="document-reader">
|
||||
<% if (document.content) { %>
|
||||
<article class="document-copy"><%= document.content %></article>
|
||||
<% } %>
|
||||
|
||||
<% if (document.filePath && documentPreviewKind) { %>
|
||||
<section class="document-preview-block">
|
||||
<div class="document-section-heading">
|
||||
<span class="material-symbols-outlined">attach_file</span>
|
||||
<strong>Xem trước: <%= document.originalFileName %></strong>
|
||||
</div>
|
||||
<% if (documentPreviewKind === 'image') { %>
|
||||
<img class="document-image-preview" src="/documents/<%= document.id %>/file" alt="<%= document.title %>">
|
||||
<% } else { %>
|
||||
<iframe
|
||||
class="document-file-preview"
|
||||
src="/documents/<%= document.id %>/file"
|
||||
title="Xem trước <%= document.originalFileName %>"
|
||||
<% if (documentPreviewKind === 'text') { %>sandbox<% } %>
|
||||
></iframe>
|
||||
<% } %>
|
||||
</section>
|
||||
<% } else if (document.filePath) { %>
|
||||
<div class="document-attachment-state">
|
||||
<span class="material-symbols-outlined">description</span>
|
||||
<div>
|
||||
<strong><%= document.originalFileName %></strong>
|
||||
<p>Trình duyệt không xem trực tiếp định dạng này. Hãy tải file để mở bằng ứng dụng phù hợp.</p>
|
||||
</div>
|
||||
<a class="btn btn-secondary" href="/documents/<%= document.id %>/file?download=1">Tải file</a>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (!document.content && !document.filePath) { %>
|
||||
<div class="table-empty">Tài liệu chưa có nội dung.</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="editDocumentModal" class="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="editDocumentModalTitle">
|
||||
<div class="modal-content wide">
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h3 id="editDocumentModalTitle">Chỉnh sửa tài liệu</h3>
|
||||
<p>Cập nhật nội dung hoặc thay thế file đính kèm hiện tại.</p>
|
||||
</div>
|
||||
<button class="icon-button subtle" type="button" data-modal-close aria-label="Đóng">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form class="modal-form" method="post" action="/documents/<%= document.id %>/edit" enctype="multipart/form-data">
|
||||
<div class="form-stack">
|
||||
<div class="form-grid">
|
||||
<label class="form-field">
|
||||
<span>Tiêu đề</span>
|
||||
<input type="text" name="title" maxlength="200" required value="<%= document.title %>">
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>Nhóm tài liệu</span>
|
||||
<select name="category" required>
|
||||
<% documentCategories.forEach((category) => { %>
|
||||
<option value="<%= category.id %>" <%= document.category === category.id ? 'selected' : '' %>><%= category.label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<label class="form-field">
|
||||
<span>Mô tả ngắn</span>
|
||||
<textarea name="summary" rows="3" maxlength="1000"><%= document.summary %></textarea>
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>Nội dung đọc trực tiếp</span>
|
||||
<textarea name="content" rows="8" maxlength="500000"><%= document.content %></textarea>
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>Thay file đính kèm</span>
|
||||
<input
|
||||
type="file"
|
||||
name="documentFile"
|
||||
accept=".pdf,.doc,.docx,.odt,.rtf,.txt,.md,.png,.jpg,.jpeg,.webp,.ppt,.pptx,.xls,.xlsx"
|
||||
>
|
||||
<small>Để trống nếu muốn giữ file hiện tại. File mới sẽ thay thế file cũ.</small>
|
||||
</label>
|
||||
<% if (document.filePath) { %>
|
||||
<label class="document-remove-option">
|
||||
<input type="checkbox" name="removeAttachment" value="1">
|
||||
<span>Xóa file hiện tại: <strong><%= document.originalFileName %></strong></span>
|
||||
</label>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-secondary" type="button" data-modal-close>Hủy</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span class="material-symbols-outlined">save</span>
|
||||
Lưu thay đổi
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include('partials/page-end') %>
|
||||
161
web-server/views/documents.ejs
Normal file
161
web-server/views/documents.ejs
Normal file
@@ -0,0 +1,161 @@
|
||||
<%- include('partials/page-start') %>
|
||||
|
||||
<section class="page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1>Tài liệu</h1>
|
||||
<p>Lưu trữ và tra cứu tài liệu giới thiệu, hướng dẫn sử dụng, quy trình và tài liệu kỹ thuật.</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-primary" type="button" data-modal-open="createDocumentModal">
|
||||
<span class="material-symbols-outlined">note_add</span>
|
||||
Thêm tài liệu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-filters">
|
||||
<label class="filter-field">
|
||||
<span>Nhóm tài liệu</span>
|
||||
<select data-filter-select data-filter-column="category" data-filter-table="documentsTable">
|
||||
<option value="">Tất cả</option>
|
||||
<% documentCategories.forEach((category) => { %>
|
||||
<option value="<%= category.id %>"><%= category.label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</label>
|
||||
<label class="filter-field wide">
|
||||
<span>Tìm kiếm</span>
|
||||
<input type="search" placeholder="Tìm theo tiêu đề, mô tả, tác giả..." data-table-search="documentsTable">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<section class="table-panel">
|
||||
<div class="table-wrap">
|
||||
<table id="documentsTable" class="data-table documents-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tài liệu</th>
|
||||
<th>Nhóm</th>
|
||||
<th>File đính kèm</th>
|
||||
<th>Cập nhật</th>
|
||||
<th>Người tạo</th>
|
||||
<th class="action-col">Thao tác</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if (documents.length === 0) { %>
|
||||
<tr>
|
||||
<td colspan="6" class="table-empty">Chưa có tài liệu. Bấm Thêm tài liệu để tạo nội dung đầu tiên.</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% documents.forEach((item) => { %>
|
||||
<tr
|
||||
data-search="<%= `${item.title} ${item.summary} ${item.createdBy} ${helpers.documentCategoryLabel(item.category)}`.toLowerCase() %>"
|
||||
data-category="<%= item.category %>"
|
||||
>
|
||||
<td class="document-title-cell">
|
||||
<a class="table-title" href="/documents/<%= item.id %>"><%= item.title %></a>
|
||||
<span class="table-subtitle"><%= item.summary || (item.hasContent ? 'Có nội dung đọc trực tiếp' : 'Tài liệu đính kèm') %></span>
|
||||
</td>
|
||||
<td><span class="badge badge-info"><%= helpers.documentCategoryLabel(item.category) %></span></td>
|
||||
<td>
|
||||
<% if (item.filePath) { %>
|
||||
<span class="document-file-name" title="<%= item.originalFileName %>"><%= item.originalFileName %></span>
|
||||
<span class="table-subtitle"><%= item.fileSize %></span>
|
||||
<% } else { %>
|
||||
<span class="table-subtitle">Không có file</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td><%= item.updatedAt %></td>
|
||||
<td><%= item.createdBy %></td>
|
||||
<td class="action-col">
|
||||
<div class="action-group">
|
||||
<a class="icon-button subtle" href="/documents/<%= item.id %>" title="Đọc tài liệu" aria-label="Đọc tài liệu <%= item.title %>">
|
||||
<span class="material-symbols-outlined">visibility</span>
|
||||
</a>
|
||||
<% if (item.filePath) { %>
|
||||
<a class="icon-button subtle" href="/documents/<%= item.id %>/file?download=1" title="Tải file" aria-label="Tải file <%= item.title %>">
|
||||
<span class="material-symbols-outlined">download</span>
|
||||
</a>
|
||||
<% } %>
|
||||
<form method="post" action="/documents/<%= item.id %>/delete" data-confirm-submit="Xóa tài liệu <%= item.title %> và file đính kèm?">
|
||||
<button class="icon-button danger" type="submit" title="Xóa tài liệu" aria-label="Xóa tài liệu <%= item.title %>">
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page-pager">
|
||||
<span>Hiển thị <%= documents.length %> tài liệu</span>
|
||||
<div>
|
||||
<button type="button" disabled>Trước</button>
|
||||
<span>Trang 1 / 1</span>
|
||||
<button type="button" disabled>Sau</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div id="createDocumentModal" class="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="createDocumentModalTitle">
|
||||
<div class="modal-content wide">
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h3 id="createDocumentModalTitle">Thêm tài liệu</h3>
|
||||
<p>Nhập nội dung để đọc trực tiếp, đính kèm file, hoặc sử dụng cả hai.</p>
|
||||
</div>
|
||||
<button class="icon-button subtle" type="button" data-modal-close aria-label="Đóng">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form class="modal-form" method="post" action="/documents" enctype="multipart/form-data">
|
||||
<div class="form-stack">
|
||||
<div class="form-grid">
|
||||
<label class="form-field">
|
||||
<span>Tiêu đề</span>
|
||||
<input type="text" name="title" maxlength="200" required placeholder="Ví dụ: Hướng dẫn cài đặt Robot">
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>Nhóm tài liệu</span>
|
||||
<select name="category" required>
|
||||
<% documentCategories.forEach((category) => { %>
|
||||
<option value="<%= category.id %>"><%= category.label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<label class="form-field">
|
||||
<span>Mô tả ngắn</span>
|
||||
<textarea name="summary" rows="3" maxlength="1000" placeholder="Nội dung chính và đối tượng sử dụng tài liệu này."></textarea>
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>Nội dung đọc trực tiếp</span>
|
||||
<textarea name="content" rows="8" maxlength="500000" placeholder="Nhập nội dung hướng dẫn tại đây. Xuống dòng và khoảng trắng sẽ được giữ nguyên khi hiển thị."></textarea>
|
||||
</label>
|
||||
<label class="form-field">
|
||||
<span>File đính kèm</span>
|
||||
<input
|
||||
type="file"
|
||||
name="documentFile"
|
||||
accept=".pdf,.doc,.docx,.odt,.rtf,.txt,.md,.png,.jpg,.jpeg,.webp,.ppt,.pptx,.xls,.xlsx"
|
||||
>
|
||||
<small>Hỗ trợ PDF, Word, OpenDocument, text/Markdown, ảnh, PowerPoint và Excel; tối đa 50 MB mặc định.</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-secondary" type="button" data-modal-close>Hủy</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span class="material-symbols-outlined">save</span>
|
||||
Lưu tài liệu
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include('partials/page-end') %>
|
||||
@@ -114,6 +114,7 @@
|
||||
data-user-updated-at="<%= user.updatedAt %>"
|
||||
data-user-package-count="<%= user.packageCount %>"
|
||||
data-user-application-count="<%= user.applicationCount %>"
|
||||
data-user-document-count="<%= user.documentCount %>"
|
||||
>
|
||||
<td>
|
||||
<span class="table-title"><%= user.name %></span>
|
||||
@@ -126,6 +127,7 @@
|
||||
<td>
|
||||
<span class="table-subtitle"><%= user.packageCount %> packages</span>
|
||||
<span class="table-subtitle"><%= user.applicationCount %> apps</span>
|
||||
<span class="table-subtitle"><%= user.documentCount %> tài liệu</span>
|
||||
</td>
|
||||
<td>
|
||||
<% if (user.id === currentUser.id) { %>
|
||||
|
||||
Reference in New Issue
Block a user