add change language
This commit is contained in:
@@ -3,6 +3,7 @@ import type { ModelVideo } from '@/api/client';
|
||||
import { fetchMockVideoById, updateMockVideo } from '@/mocks/videos';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
videoId: string;
|
||||
@@ -16,6 +17,7 @@ const toast = useAppToast();
|
||||
const video = ref<ModelVideo | null>(null);
|
||||
const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const { t } = useI18n();
|
||||
|
||||
const form = ref({
|
||||
title: '',
|
||||
@@ -43,7 +45,12 @@ const fetchVideo = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch video:', error);
|
||||
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to load video details', life: 3000 });
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: t('video.detailModal.toast.loadErrorSummary'),
|
||||
detail: t('video.detailModal.toast.loadErrorDetail'),
|
||||
life: 3000
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -52,7 +59,7 @@ const fetchVideo = async () => {
|
||||
const validate = (): boolean => {
|
||||
errors.value = {};
|
||||
if (!form.value.title.trim()) {
|
||||
errors.value.title = 'Title is required.';
|
||||
errors.value.title = t('video.detailModal.errors.titleRequired');
|
||||
}
|
||||
return Object.keys(errors.value).length === 0;
|
||||
};
|
||||
@@ -68,11 +75,21 @@ const onFormSubmit = async () => {
|
||||
video.value.description = form.value.description;
|
||||
}
|
||||
|
||||
toast.add({ severity: 'success', summary: 'Success', detail: 'Video updated successfully', life: 3000 });
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: t('video.detailModal.toast.saveSuccessSummary'),
|
||||
detail: t('video.detailModal.toast.saveSuccessDetail'),
|
||||
life: 3000
|
||||
});
|
||||
emit('close');
|
||||
} catch (error) {
|
||||
console.error('Failed to save video:', error);
|
||||
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to save changes', life: 3000 });
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: t('video.detailModal.toast.saveErrorSummary'),
|
||||
detail: t('video.detailModal.toast.saveErrorDetail'),
|
||||
life: 3000
|
||||
});
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
@@ -89,7 +106,12 @@ const canUploadSubtitle = computed(() => {
|
||||
|
||||
const handleUploadSubtitle = () => {
|
||||
if (!canUploadSubtitle.value) return;
|
||||
toast.add({ severity: 'info', summary: 'Info', detail: 'Subtitle upload not yet implemented', life: 3000 });
|
||||
toast.add({
|
||||
severity: 'info',
|
||||
summary: t('video.detailModal.toast.subtitleInfoSummary'),
|
||||
detail: t('video.detailModal.toast.subtitleInfoDetail'),
|
||||
life: 3000
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.videoId, (newId) => {
|
||||
@@ -102,7 +124,7 @@ watch(() => props.videoId, (newId) => {
|
||||
|
||||
<template>
|
||||
<AppDialog :visible="!!videoId" @update:visible="emit('close')" max-width-class="max-w-xl"
|
||||
:title="loading ? '' : 'Edit video'">
|
||||
:title="loading ? '' : t('video.detailModal.title')">
|
||||
|
||||
<!-- Loading Skeleton -->
|
||||
<div v-if="loading" class="flex flex-col gap-4">
|
||||
@@ -144,15 +166,15 @@ watch(() => props.videoId, (newId) => {
|
||||
<form v-else @submit.prevent="onFormSubmit" class="flex flex-col gap-4">
|
||||
<!-- Title -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="edit-title" class="text-sm font-medium">Title</label>
|
||||
<AppInput id="edit-title" v-model="form.title" placeholder="Enter video title" />
|
||||
<label for="edit-title" class="text-sm font-medium">{{ t('video.detailModal.titleLabel') }}</label>
|
||||
<AppInput id="edit-title" v-model="form.title" :placeholder="t('video.detailModal.titlePlaceholder')" />
|
||||
<p v-if="errors.title" class="text-xs text-red-500 mt-0.5">{{ errors.title }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="edit-description" class="text-sm font-medium">Description</label>
|
||||
<textarea id="edit-description" v-model="form.description" placeholder="Enter video description"
|
||||
<label for="edit-description" class="text-sm font-medium">{{ t('video.detailModal.descriptionLabel') }}</label>
|
||||
<textarea id="edit-description" v-model="form.description" :placeholder="t('video.detailModal.descriptionPlaceholder')"
|
||||
rows="4"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent resize-y" />
|
||||
<p v-if="errors.description" class="text-xs text-red-500 mt-0.5">{{ errors.description }}</p>
|
||||
@@ -161,20 +183,20 @@ watch(() => props.videoId, (newId) => {
|
||||
<!-- Subtitles Section -->
|
||||
<div class="flex flex-col gap-3 border-t-2 border-gray-200 pt-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="text-sm font-medium">Subtitles</label>
|
||||
<label class="text-sm font-medium">{{ t('video.detailModal.subtitlesTitle') }}</label>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
|
||||
0 tracks
|
||||
{{ t('video.detailModal.subtitleTracks', { count: 0 }) }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">No subtitles uploaded yet</p>
|
||||
<p class="text-sm text-muted-foreground">{{ t('video.detailModal.noSubtitles') }}</p>
|
||||
|
||||
<!-- Upload Subtitle Form -->
|
||||
<div class="flex flex-col gap-3 rounded-lg border border-gray-200 p-3">
|
||||
<label class="text-sm font-medium">Upload Subtitle</label>
|
||||
<label class="text-sm font-medium">{{ t('video.detailModal.uploadSubtitle') }}</label>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="subtitle-file" class="text-xs font-medium">
|
||||
Subtitle File (VTT, SRT, ASS, SSA)
|
||||
{{ t('video.detailModal.subtitleFile') }}
|
||||
</label>
|
||||
<input id="subtitle-file" type="file"
|
||||
accept=".vtt,.srt,.ass,.ssa,text/vtt,text/srt,application/x-subrip"
|
||||
@@ -184,27 +206,27 @@ watch(() => props.videoId, (newId) => {
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="subtitle-language" class="text-xs font-medium">Language Code *</label>
|
||||
<AppInput id="subtitle-language" v-model="subtitleForm.language" placeholder="en, vi, etc."
|
||||
<label for="subtitle-language" class="text-xs font-medium">{{ t('video.detailModal.languageCode') }}</label>
|
||||
<AppInput id="subtitle-language" v-model="subtitleForm.language" :placeholder="t('video.detailModal.languagePlaceholder')"
|
||||
:maxlength="10" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="subtitle-name" class="text-xs font-medium">Display Name (Optional)</label>
|
||||
<label for="subtitle-name" class="text-xs font-medium">{{ t('video.detailModal.displayName') }}</label>
|
||||
<AppInput id="subtitle-name" v-model="subtitleForm.displayName"
|
||||
placeholder="English, Tiếng Việt, etc." />
|
||||
:placeholder="t('video.detailModal.displayNamePlaceholder')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AppButton variant="secondary" class="w-full" :disabled="!canUploadSubtitle" @click="handleUploadSubtitle">
|
||||
Upload Subtitle
|
||||
{{ t('video.detailModal.uploadSubtitleButton') }}
|
||||
</AppButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer inside Form so submit works -->
|
||||
<div class="flex justify-end gap-2 border-t border-gray-200 pt-4">
|
||||
<AppButton variant="ghost" type="button" @click="emit('close')">Cancel</AppButton>
|
||||
<AppButton type="submit" :loading="saving">Save Changes</AppButton>
|
||||
<AppButton variant="ghost" type="button" @click="emit('close')">{{ t('video.detailModal.cancel') }}</AppButton>
|
||||
<AppButton type="submit" :loading="saving">{{ t('video.detailModal.saveChanges') }}</AppButton>
|
||||
</div>
|
||||
</form>
|
||||
</AppDialog>
|
||||
|
||||
Reference in New Issue
Block a user