feat: add admin detail policy actions
This commit is contained in:
@@ -13,6 +13,8 @@ import type {
|
|||||||
AdminFileInsightSeverity,
|
AdminFileInsightSeverity,
|
||||||
AdminFilePatchPayload,
|
AdminFilePatchPayload,
|
||||||
AdminFileListParams,
|
AdminFileListParams,
|
||||||
|
AdminFilePolicyAction,
|
||||||
|
AdminFilePolicyActionRequest,
|
||||||
AdminFileStatusFilter,
|
AdminFileStatusFilter,
|
||||||
AdminFileSummary,
|
AdminFileSummary,
|
||||||
AdminFileTypeFilter,
|
AdminFileTypeFilter,
|
||||||
@@ -61,6 +63,14 @@ const isLegacyEndpointUnavailable = (error: unknown) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const legacyForeverExpiresAt = '2099-12-31T23:59'
|
const legacyForeverExpiresAt = '2099-12-31T23:59'
|
||||||
|
const detailPolicyDownloadLimit = 5
|
||||||
|
const dayInMilliseconds = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
|
type DetailPolicyActionOption = {
|
||||||
|
action: AdminFilePolicyAction
|
||||||
|
label: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
const emptyBatchEditForm = (): AdminBatchEditForm => ({
|
const emptyBatchEditForm = (): AdminBatchEditForm => ({
|
||||||
mode: 'expiresAt',
|
mode: 'expiresAt',
|
||||||
@@ -68,6 +78,18 @@ const emptyBatchEditForm = (): AdminBatchEditForm => ({
|
|||||||
expired_count: null
|
expired_count: null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const padDatePart = (value: number) => String(value).padStart(2, '0')
|
||||||
|
|
||||||
|
const formatLocalDateTime = (date: Date) => {
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = padDatePart(date.getMonth() + 1)
|
||||||
|
const day = padDatePart(date.getDate())
|
||||||
|
const hours = padDatePart(date.getHours())
|
||||||
|
const minutes = padDatePart(date.getMinutes())
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}T${hours}:${minutes}`
|
||||||
|
}
|
||||||
|
|
||||||
export function useAdminFiles() {
|
export function useAdminFiles() {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const alertStore = useAlertStore()
|
const alertStore = useAlertStore()
|
||||||
@@ -108,6 +130,7 @@ export function useAdminFiles() {
|
|||||||
const showFileDetailModal = ref(false)
|
const showFileDetailModal = ref(false)
|
||||||
const selectedFileDetail = ref<AdminFileDetailViewItem | null>(null)
|
const selectedFileDetail = ref<AdminFileDetailViewItem | null>(null)
|
||||||
const isDetailLoading = ref(false)
|
const isDetailLoading = ref(false)
|
||||||
|
const isDetailPolicyActionRunning = ref(false)
|
||||||
const downloadingFileId = ref<number | null>(null)
|
const downloadingFileId = ref<number | null>(null)
|
||||||
const selectedFileIds = ref<Set<number>>(new Set())
|
const selectedFileIds = ref<Set<number>>(new Set())
|
||||||
const isBatchDeleting = ref(false)
|
const isBatchDeleting = ref(false)
|
||||||
@@ -145,6 +168,33 @@ export function useAdminFiles() {
|
|||||||
params.value.type !== 'all'
|
params.value.type !== 'all'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const detailPolicyActionOptions = computed<DetailPolicyActionOption[]>(() => [
|
||||||
|
{
|
||||||
|
action: 'extend_24h',
|
||||||
|
label: t('fileManage.policyActions.extend24h'),
|
||||||
|
description: t('fileManage.policyActionDescriptions.extend24h')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'extend_7d',
|
||||||
|
label: t('fileManage.policyActions.extend7d'),
|
||||||
|
description: t('fileManage.policyActionDescriptions.extend7d')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'make_permanent',
|
||||||
|
label: t('fileManage.policyActions.makePermanent'),
|
||||||
|
description: t('fileManage.policyActionDescriptions.makePermanent')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'reset_download_limit',
|
||||||
|
label: t('fileManage.policyActions.resetDownloadLimit', {
|
||||||
|
count: detailPolicyDownloadLimit
|
||||||
|
}),
|
||||||
|
description: t('fileManage.policyActionDescriptions.resetDownloadLimit', {
|
||||||
|
count: detailPolicyDownloadLimit
|
||||||
|
})
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const inferIsText = (file: FileListItem) => {
|
const inferIsText = (file: FileListItem) => {
|
||||||
if (typeof file.isText === 'boolean') return file.isText
|
if (typeof file.isText === 'boolean') return file.isText
|
||||||
if (typeof file.is_text === 'boolean') return file.is_text
|
if (typeof file.is_text === 'boolean') return file.is_text
|
||||||
@@ -158,7 +208,7 @@ export function useAdminFiles() {
|
|||||||
if (
|
if (
|
||||||
file.expired_count !== null &&
|
file.expired_count !== null &&
|
||||||
file.expired_count !== undefined &&
|
file.expired_count !== undefined &&
|
||||||
file.expired_count <= 0
|
file.expired_count === 0
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -463,6 +513,84 @@ export function useAdminFiles() {
|
|||||||
return patchPayload
|
return patchPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buildPolicyActionRequest = (
|
||||||
|
file: AdminFileDetailViewItem,
|
||||||
|
action: AdminFilePolicyAction
|
||||||
|
): AdminFilePolicyActionRequest => {
|
||||||
|
const request: AdminFilePolicyActionRequest = {
|
||||||
|
id: file.id,
|
||||||
|
action
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'reset_download_limit') {
|
||||||
|
request.downloadLimit = detailPolicyDownloadLimit
|
||||||
|
request.download_limit = detailPolicyDownloadLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPolicyActionBaseDate = (file: AdminFileDetailViewItem) => {
|
||||||
|
const now = new Date()
|
||||||
|
const expiredAt = file.expired_at ? new Date(file.expired_at) : null
|
||||||
|
if (!expiredAt || Number.isNaN(expiredAt.getTime()) || expiredAt.getTime() < now.getTime()) {
|
||||||
|
return now
|
||||||
|
}
|
||||||
|
return expiredAt
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildLegacyPolicyActionPayload = (
|
||||||
|
file: AdminFileDetailViewItem,
|
||||||
|
action: AdminFilePolicyAction
|
||||||
|
): AdminFilePatchPayload => {
|
||||||
|
if (action === 'make_permanent') {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
expired_at: legacyForeverExpiresAt,
|
||||||
|
expired_count: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'reset_download_limit') {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
expired_count: detailPolicyDownloadLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetDays = action === 'extend_7d' ? 7 : 1
|
||||||
|
const nextExpiredAt = new Date(
|
||||||
|
getPolicyActionBaseDate(file).getTime() + offsetDays * dayInMilliseconds
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
expired_at: formatLocalDateTime(nextExpiredAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshSelectedFileDetail = async (fileId: number, detail?: AdminFileDetailResponse) => {
|
||||||
|
if (detail) {
|
||||||
|
selectedFileDetail.value = createFileDetailViewItem(detail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackFile = tableData.value.find((file) => file.id === fileId)
|
||||||
|
if (fallbackFile) {
|
||||||
|
selectedFileDetail.value = createFileDetailViewItem(fallbackFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await FileService.getAdminFileDetail(fileId)
|
||||||
|
if (response.detail) {
|
||||||
|
selectedFileDetail.value = createFileDetailViewItem(response.detail)
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (!isLegacyEndpointUnavailable(error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadFiles = async () => {
|
const loadFiles = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -775,6 +903,36 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyDetailPolicyAction = async (action: AdminFilePolicyAction) => {
|
||||||
|
const file = selectedFileDetail.value
|
||||||
|
if (!file || isDetailPolicyActionRunning.value) return
|
||||||
|
|
||||||
|
isDetailPolicyActionRunning.value = true
|
||||||
|
try {
|
||||||
|
let nextDetail: AdminFileDetailResponse | undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await FileService.applyAdminFilePolicyAction(
|
||||||
|
buildPolicyActionRequest(file, action)
|
||||||
|
)
|
||||||
|
nextDetail = response.detail
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (!isLegacyEndpointUnavailable(error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
await FileService.updateFile(buildLegacyPolicyActionPayload(file, action))
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadFiles()
|
||||||
|
await refreshSelectedFileDetail(file.id, nextDetail)
|
||||||
|
alertStore.showAlert(t('fileManage.policyActionSuccess'), 'success')
|
||||||
|
} catch (error: unknown) {
|
||||||
|
alertStore.showAlert(getErrorMessage(error, t('fileManage.policyActionFailed')), 'error')
|
||||||
|
} finally {
|
||||||
|
isDetailPolicyActionRunning.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const closeFileDetail = () => {
|
const closeFileDetail = () => {
|
||||||
detailRequestSerial += 1
|
detailRequestSerial += 1
|
||||||
showFileDetailModal.value = false
|
showFileDetailModal.value = false
|
||||||
@@ -846,9 +1004,11 @@ export function useAdminFiles() {
|
|||||||
isBatchUpdating,
|
isBatchUpdating,
|
||||||
isCurrentPagePartiallySelected,
|
isCurrentPagePartiallySelected,
|
||||||
isDetailLoading,
|
isDetailLoading,
|
||||||
|
isDetailPolicyActionRunning,
|
||||||
isPreviewLoading,
|
isPreviewLoading,
|
||||||
isSaving,
|
isSaving,
|
||||||
batchEditForm,
|
batchEditForm,
|
||||||
|
detailPolicyActionOptions,
|
||||||
downloadingFileId,
|
downloadingFileId,
|
||||||
hasSelectedFiles,
|
hasSelectedFiles,
|
||||||
params,
|
params,
|
||||||
@@ -880,6 +1040,7 @@ export function useAdminFiles() {
|
|||||||
exportPreviewText,
|
exportPreviewText,
|
||||||
handlePageChange,
|
handlePageChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
|
applyDetailPolicyAction,
|
||||||
handleBatchUpdate,
|
handleBatchUpdate,
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
loadFiles,
|
loadFiles,
|
||||||
|
|||||||
@@ -448,6 +448,20 @@ export default {
|
|||||||
batchUpdatePartialSuccess: 'Updated {count} files, {failed} failed',
|
batchUpdatePartialSuccess: 'Updated {count} files, {failed} failed',
|
||||||
batchUpdateFailed: 'Batch update failed',
|
batchUpdateFailed: 'Batch update failed',
|
||||||
batchUpdateNoFields: 'Choose a policy to update',
|
batchUpdateNoFields: 'Choose a policy to update',
|
||||||
|
policyActionSuccess: 'File policy updated',
|
||||||
|
policyActionFailed: 'Failed to apply policy action',
|
||||||
|
policyActions: {
|
||||||
|
extend24h: 'Extend 24h',
|
||||||
|
extend7d: 'Extend 7d',
|
||||||
|
makePermanent: 'Make Permanent',
|
||||||
|
resetDownloadLimit: 'Reset to {count}'
|
||||||
|
},
|
||||||
|
policyActionDescriptions: {
|
||||||
|
extend24h: 'Add one day from the active expiration',
|
||||||
|
extend7d: 'Good for a one-week temporary extension',
|
||||||
|
makePermanent: 'Clear expiration time and retrieval limits',
|
||||||
|
resetDownloadLimit: 'Restore retrievals to {count}'
|
||||||
|
},
|
||||||
applyBatchEdit: 'Apply Changes',
|
applyBatchEdit: 'Apply Changes',
|
||||||
overview: 'Overview',
|
overview: 'Overview',
|
||||||
policyInfo: 'Policy',
|
policyInfo: 'Policy',
|
||||||
|
|||||||
@@ -446,6 +446,20 @@ export default {
|
|||||||
batchUpdatePartialSuccess: '已更新 {count} 个文件,{failed} 个失败',
|
batchUpdatePartialSuccess: '已更新 {count} 个文件,{failed} 个失败',
|
||||||
batchUpdateFailed: '批量更新失败',
|
batchUpdateFailed: '批量更新失败',
|
||||||
batchUpdateNoFields: '请选择要更新的策略',
|
batchUpdateNoFields: '请选择要更新的策略',
|
||||||
|
policyActionSuccess: '文件策略已更新',
|
||||||
|
policyActionFailed: '策略动作执行失败',
|
||||||
|
policyActions: {
|
||||||
|
extend24h: '延长 24 小时',
|
||||||
|
extend7d: '延长 7 天',
|
||||||
|
makePermanent: '设为永久',
|
||||||
|
resetDownloadLimit: '重置为 {count} 次'
|
||||||
|
},
|
||||||
|
policyActionDescriptions: {
|
||||||
|
extend24h: '从当前有效期继续追加一天',
|
||||||
|
extend7d: '适合临时续期一周',
|
||||||
|
makePermanent: '清空过期时间和次数限制',
|
||||||
|
resetDownloadLimit: '恢复可取件次数到 {count} 次'
|
||||||
|
},
|
||||||
applyBatchEdit: '应用更改',
|
applyBatchEdit: '应用更改',
|
||||||
overview: '概览',
|
overview: '概览',
|
||||||
policyInfo: '策略信息',
|
policyInfo: '策略信息',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
AdminFilePatchPayload,
|
AdminFilePatchPayload,
|
||||||
AdminFileDetailResponse,
|
AdminFileDetailResponse,
|
||||||
AdminFileListParams,
|
AdminFileListParams,
|
||||||
|
AdminFilePolicyActionRequest,
|
||||||
AdminFilePreviewResponse,
|
AdminFilePreviewResponse,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
ChunkUploadCompleteRequest,
|
ChunkUploadCompleteRequest,
|
||||||
@@ -37,6 +38,11 @@ const toUrlEncodedForm = (data: Record<string, string | number>) => {
|
|||||||
return form
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMethodFallbackError = (error: unknown) => {
|
||||||
|
const status = (error as { response?: { status?: number } })?.response?.status
|
||||||
|
return status === 404 || status === 405
|
||||||
|
}
|
||||||
|
|
||||||
export class FileService {
|
export class FileService {
|
||||||
static async uploadFile(
|
static async uploadFile(
|
||||||
file: File,
|
file: File,
|
||||||
@@ -139,6 +145,20 @@ export class FileService {
|
|||||||
return api.patch('/admin/file/update', data)
|
return api.patch('/admin/file/update', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async applyAdminFilePolicyAction(
|
||||||
|
data: AdminFilePolicyActionRequest
|
||||||
|
): Promise<ApiResponse<AdminFileDetailResponse>> {
|
||||||
|
try {
|
||||||
|
return await api.patch('/admin/file/policy-action', data)
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (!isMethodFallbackError(error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.post('/admin/file/policy-action', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async updateAdminFiles(
|
static async updateAdminFiles(
|
||||||
data: AdminBatchUpdateFilesRequest
|
data: AdminBatchUpdateFilesRequest
|
||||||
): Promise<ApiResponse<AdminBatchUpdateFilesResponse>> {
|
): Promise<ApiResponse<AdminBatchUpdateFilesResponse>> {
|
||||||
|
|||||||
@@ -92,6 +92,19 @@ export interface AdminFilePatchPayload {
|
|||||||
expired_count?: number | null
|
expired_count?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AdminFilePolicyAction =
|
||||||
|
| 'extend_24h'
|
||||||
|
| 'extend_7d'
|
||||||
|
| 'make_permanent'
|
||||||
|
| 'reset_download_limit'
|
||||||
|
|
||||||
|
export interface AdminFilePolicyActionRequest {
|
||||||
|
id: number
|
||||||
|
action: AdminFilePolicyAction
|
||||||
|
downloadLimit?: number
|
||||||
|
download_limit?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileListResponse {
|
export interface FileListResponse {
|
||||||
data: FileListItem[]
|
data: FileListItem[]
|
||||||
total: number
|
total: number
|
||||||
|
|||||||
@@ -620,6 +620,35 @@
|
|||||||
{{ reason }}
|
{{ reason }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
|
<button
|
||||||
|
v-for="action in detailPolicyActionOptions"
|
||||||
|
:key="action.action"
|
||||||
|
type="button"
|
||||||
|
class="flex min-h-16 items-start gap-2 rounded-lg border px-3 py-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
|
:class="detailPolicyActionClass"
|
||||||
|
:disabled="isDetailPolicyActionRunning || isDetailLoading"
|
||||||
|
:title="action.description"
|
||||||
|
@click="applyDetailPolicyAction(action.action)"
|
||||||
|
>
|
||||||
|
<RefreshCwIcon
|
||||||
|
v-if="isDetailPolicyActionRunning"
|
||||||
|
class="mt-0.5 h-4 w-4 shrink-0 animate-spin"
|
||||||
|
/>
|
||||||
|
<component
|
||||||
|
:is="getDetailPolicyActionIcon(action.action)"
|
||||||
|
v-else
|
||||||
|
class="mt-0.5 h-4 w-4 shrink-0"
|
||||||
|
/>
|
||||||
|
<span class="min-w-0">
|
||||||
|
<span class="block text-sm font-medium">{{ action.label }}</span>
|
||||||
|
<span class="mt-0.5 block text-xs leading-4" :class="[mutedTextClass]">
|
||||||
|
{{ action.description }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="space-y-3">
|
<section class="space-y-3">
|
||||||
@@ -969,6 +998,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import type {
|
import type {
|
||||||
AdminBatchEditMode,
|
AdminBatchEditMode,
|
||||||
AdminFileInsightSeverity,
|
AdminFileInsightSeverity,
|
||||||
|
AdminFilePolicyAction,
|
||||||
AdminFileSortBy,
|
AdminFileSortBy,
|
||||||
AdminFileSortOrder,
|
AdminFileSortOrder,
|
||||||
AdminFileStatusFilter,
|
AdminFileStatusFilter,
|
||||||
@@ -987,8 +1017,10 @@ import {
|
|||||||
FileTextIcon,
|
FileTextIcon,
|
||||||
FilterIcon,
|
FilterIcon,
|
||||||
HardDriveIcon,
|
HardDriveIcon,
|
||||||
|
InfinityIcon,
|
||||||
PencilIcon,
|
PencilIcon,
|
||||||
RefreshCwIcon,
|
RefreshCwIcon,
|
||||||
|
RotateCcwIcon,
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
XIcon
|
XIcon
|
||||||
@@ -1026,9 +1058,11 @@ const {
|
|||||||
isBatchUpdating,
|
isBatchUpdating,
|
||||||
isCurrentPagePartiallySelected,
|
isCurrentPagePartiallySelected,
|
||||||
isDetailLoading,
|
isDetailLoading,
|
||||||
|
isDetailPolicyActionRunning,
|
||||||
isPreviewLoading,
|
isPreviewLoading,
|
||||||
isSaving,
|
isSaving,
|
||||||
batchEditForm,
|
batchEditForm,
|
||||||
|
detailPolicyActionOptions,
|
||||||
downloadingFileId,
|
downloadingFileId,
|
||||||
hasSelectedFiles,
|
hasSelectedFiles,
|
||||||
params,
|
params,
|
||||||
@@ -1059,6 +1093,7 @@ const {
|
|||||||
exportPreviewText,
|
exportPreviewText,
|
||||||
handlePageChange,
|
handlePageChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
|
applyDetailPolicyAction,
|
||||||
handleBatchUpdate,
|
handleBatchUpdate,
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
loadFiles,
|
loadFiles,
|
||||||
@@ -1089,6 +1124,11 @@ const detailActionClass = computed(() =>
|
|||||||
? 'border-gray-700 bg-gray-700/50 text-gray-300 hover:border-gray-600 hover:bg-gray-700'
|
? 'border-gray-700 bg-gray-700/50 text-gray-300 hover:border-gray-600 hover:bg-gray-700'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50'
|
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50'
|
||||||
)
|
)
|
||||||
|
const detailPolicyActionClass = computed(() =>
|
||||||
|
isDarkMode.value
|
||||||
|
? 'border-gray-700 bg-gray-800/70 text-gray-200 hover:border-blue-500/40 hover:bg-blue-500/10'
|
||||||
|
: 'border-gray-200 bg-white text-gray-700 hover:border-blue-200 hover:bg-blue-50'
|
||||||
|
)
|
||||||
|
|
||||||
type DetailInfoItem = {
|
type DetailInfoItem = {
|
||||||
label: string
|
label: string
|
||||||
@@ -1291,6 +1331,16 @@ const batchEditModeOptions = computed<
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const detailPolicyActionIconMap: Record<AdminFilePolicyAction, Component> = {
|
||||||
|
extend_24h: ClockIcon,
|
||||||
|
extend_7d: ClockIcon,
|
||||||
|
make_permanent: InfinityIcon,
|
||||||
|
reset_download_limit: RotateCcwIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDetailPolicyActionIcon = (action: AdminFilePolicyAction) =>
|
||||||
|
detailPolicyActionIconMap[action]
|
||||||
|
|
||||||
const getPillClass = (active: boolean) => {
|
const getPillClass = (active: boolean) => {
|
||||||
if (active) return 'bg-indigo-600 text-white'
|
if (active) return 'bg-indigo-600 text-white'
|
||||||
return isDarkMode.value
|
return isDarkMode.value
|
||||||
|
|||||||
Reference in New Issue
Block a user