fix(#107): trash/recent/favorites list view rendering bugs
Trash view:
- Fix 'Invalid Date': use item.trashed_at (ISO 8601) instead of item.deleted_at * 1000
- Fix literal i18n key 'files.file_types.file': determine type from file extension
(pdf, image, video, audio, text, document) since trash DTO has no mime_type
- Fix column alignment: remove checkbox from trash header (not applicable),
add .trash-header CSS class matching the 5-column grid layout
Recent view:
- Fix header/row column misalignment: add empty placeholder div for indicator
column and .recent-header CSS class matching the 5-column grid
- Fix missing i18n key: use 'recent.accessed' instead of 'files.last_accessed'
- Fix default typeLabel not internationalized: use i18n.t('files.file_types.document')
Favorites view:
- Fix header/row column misalignment: add empty placeholder div for indicator
column and .favorites-header CSS class matching the 5-column grid
- Fix default typeLabel not internationalized: use i18n.t('files.file_types.document')
Bump SW cache to v11.
This commit is contained in:
@@ -47,6 +47,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Adjustments for list view */
|
/* Adjustments for list view */
|
||||||
|
.list-header.favorites-header {
|
||||||
|
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
||||||
|
}
|
||||||
|
|
||||||
.file-item.favorite-item {
|
.file-item.favorite-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
||||||
|
|||||||
@@ -36,6 +36,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Adjustments for list view */
|
/* Adjustments for list view */
|
||||||
|
.list-header.recent-header {
|
||||||
|
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
||||||
|
}
|
||||||
|
|
||||||
.file-item.recent-item {
|
.file-item.recent-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px;
|
||||||
|
|||||||
@@ -1811,9 +1811,13 @@ select:focus {
|
|||||||
background-color: #fff0ec;
|
background-color: #fff0ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For trash mode, adjust columns */
|
/* For trash mode, adjust columns (5 cols: name, type, path, date, actions) */
|
||||||
|
.list-header.trash-header {
|
||||||
|
grid-template-columns: minmax(180px, 1.5fr) 0.5fr 1fr 140px 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.trash-item.file-item {
|
.trash-item.file-item {
|
||||||
grid-template-columns: minmax(180px, 1.5fr) 0.5fr 1fr 120px 100px;
|
grid-template-columns: minmax(180px, 1.5fr) 0.5fr 1fr 140px 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-item:hover {
|
.file-item:hover {
|
||||||
|
|||||||
+35
-9
@@ -874,8 +874,7 @@ async function loadTrashItems() {
|
|||||||
if (window.multiSelect) window.multiSelect.clear();
|
if (window.multiSelect) window.multiSelect.clear();
|
||||||
elements.filesGrid.innerHTML = '';
|
elements.filesGrid.innerHTML = '';
|
||||||
elements.filesListView.innerHTML = `
|
elements.filesListView.innerHTML = `
|
||||||
<div class="list-header">
|
<div class="list-header trash-header">
|
||||||
<div class="list-header-checkbox"><input type="checkbox" id="select-all-checkbox" title="Select all"></div>
|
|
||||||
<div data-i18n="files.name">Name</div>
|
<div data-i18n="files.name">Name</div>
|
||||||
<div data-i18n="files.type">Type</div>
|
<div data-i18n="files.type">Type</div>
|
||||||
<div data-i18n="trash.original_location">Original location</div>
|
<div data-i18n="trash.original_location">Original location</div>
|
||||||
@@ -924,17 +923,44 @@ async function loadTrashItems() {
|
|||||||
*/
|
*/
|
||||||
function addTrashItemToView(item) {
|
function addTrashItemToView(item) {
|
||||||
const isFile = item.item_type === 'file';
|
const isFile = item.item_type === 'file';
|
||||||
const iconClass = isFile ? 'fas fa-file' : 'fas fa-folder';
|
|
||||||
|
|
||||||
// Format date
|
// Format date - backend sends trashed_at as ISO 8601 string
|
||||||
const deletedDate = new Date(item.deleted_at * 1000);
|
const deletedDate = new Date(item.trashed_at);
|
||||||
const formattedDate = deletedDate.toLocaleDateString() + ' ' +
|
const formattedDate = deletedDate.toLocaleDateString() + ' ' +
|
||||||
deletedDate.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
deletedDate.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||||
|
|
||||||
// Item type label
|
// Determine type label and icon from extension (trash DTO has no mime_type)
|
||||||
const typeLabel = isFile ?
|
let typeLabel;
|
||||||
(window.i18n ? window.i18n.t('files.file_types.file') : 'File') :
|
let iconClass;
|
||||||
(window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder');
|
if (!isFile) {
|
||||||
|
iconClass = 'fas fa-folder';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder';
|
||||||
|
} else {
|
||||||
|
const ext = (item.name.split('.').pop() || '').toLowerCase();
|
||||||
|
const imageExts = ['jpg','jpeg','png','gif','bmp','svg','webp','ico','tiff'];
|
||||||
|
const videoExts = ['mp4','avi','mkv','mov','wmv','flv','webm'];
|
||||||
|
const audioExts = ['mp3','wav','ogg','flac','aac','wma','m4a'];
|
||||||
|
const textExts = ['txt','md','csv','log','ini','cfg','conf'];
|
||||||
|
if (ext === 'pdf') {
|
||||||
|
iconClass = 'fas fa-file-pdf';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.pdf') : 'PDF';
|
||||||
|
} else if (imageExts.includes(ext)) {
|
||||||
|
iconClass = 'fas fa-file-image';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.image') : 'Image';
|
||||||
|
} else if (videoExts.includes(ext)) {
|
||||||
|
iconClass = 'fas fa-file-video';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.video') : 'Video';
|
||||||
|
} else if (audioExts.includes(ext)) {
|
||||||
|
iconClass = 'fas fa-file-audio';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.audio') : 'Audio';
|
||||||
|
} else if (textExts.includes(ext)) {
|
||||||
|
iconClass = 'fas fa-file-alt';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.text') : 'Text';
|
||||||
|
} else {
|
||||||
|
iconClass = 'fas fa-file';
|
||||||
|
typeLabel = window.i18n ? window.i18n.t('files.file_types.document') : 'Document';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Grid view element
|
// Grid view element
|
||||||
const gridElement = document.createElement('div');
|
const gridElement = document.createElement('div');
|
||||||
|
|||||||
@@ -320,7 +320,8 @@ const favorites = {
|
|||||||
|
|
||||||
filesGrid.innerHTML = '';
|
filesGrid.innerHTML = '';
|
||||||
filesListView.innerHTML = `
|
filesListView.innerHTML = `
|
||||||
<div class="list-header">
|
<div class="list-header favorites-header">
|
||||||
|
<div></div>
|
||||||
<div data-i18n="files.name">Name</div>
|
<div data-i18n="files.name">Name</div>
|
||||||
<div data-i18n="files.type">Type</div>
|
<div data-i18n="files.type">Type</div>
|
||||||
<div data-i18n="files.size">Size</div>
|
<div data-i18n="files.size">Size</div>
|
||||||
@@ -556,7 +557,7 @@ const favorites = {
|
|||||||
// Determine icon and type
|
// Determine icon and type
|
||||||
let iconClass = 'fas fa-file';
|
let iconClass = 'fas fa-file';
|
||||||
let iconSpecialClass = '';
|
let iconSpecialClass = '';
|
||||||
let typeLabel = 'Document';
|
let typeLabel = window.i18n ? window.i18n.t('files.file_types.document') : 'Document';
|
||||||
|
|
||||||
if (file.mime_type) {
|
if (file.mime_type) {
|
||||||
if (file.mime_type.startsWith('image/')) {
|
if (file.mime_type.startsWith('image/')) {
|
||||||
|
|||||||
+4
-3
@@ -109,11 +109,12 @@ const recent = {
|
|||||||
|
|
||||||
filesGrid.innerHTML = '';
|
filesGrid.innerHTML = '';
|
||||||
filesListView.innerHTML = `
|
filesListView.innerHTML = `
|
||||||
<div class="list-header">
|
<div class="list-header recent-header">
|
||||||
|
<div></div>
|
||||||
<div data-i18n="files.name">Name</div>
|
<div data-i18n="files.name">Name</div>
|
||||||
<div data-i18n="files.type">Type</div>
|
<div data-i18n="files.type">Type</div>
|
||||||
<div data-i18n="files.size">Size</div>
|
<div data-i18n="files.size">Size</div>
|
||||||
<div data-i18n="files.last_accessed">Last accessed</div>
|
<div data-i18n="recent.accessed">Accessed</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ const recent = {
|
|||||||
// Determine icon and type
|
// Determine icon and type
|
||||||
let iconClass = 'fas fa-file';
|
let iconClass = 'fas fa-file';
|
||||||
let iconSpecialClass = '';
|
let iconSpecialClass = '';
|
||||||
let typeLabel = 'Document';
|
let typeLabel = window.i18n ? window.i18n.t('files.file_types.document') : 'Document';
|
||||||
|
|
||||||
if (file.mime_type) {
|
if (file.mime_type) {
|
||||||
if (file.mime_type.startsWith('image/')) {
|
if (file.mime_type.startsWith('image/')) {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// OxiCloud Service Worker
|
// OxiCloud Service Worker
|
||||||
const CACHE_NAME = 'oxicloud-cache-v10';
|
const CACHE_NAME = 'oxicloud-cache-v11';
|
||||||
const ASSETS_TO_CACHE = [
|
const ASSETS_TO_CACHE = [
|
||||||
'/',
|
'/',
|
||||||
'/index.html',
|
'/index.html',
|
||||||
|
|||||||
Reference in New Issue
Block a user