refactor: use vuetify form controls

This commit is contained in:
2026-05-07 19:38:51 -04:00
parent 6ac05e1a10
commit 4aaa1a7f90
21 changed files with 724 additions and 774 deletions

View File

@@ -1,5 +1,5 @@
<script setup>
import { computed, reactive, ref } from 'vue';
import { computed, reactive } from 'vue';
import { mdiAt, mdiClose, mdiImagePlusOutline, mdiLockOutline, mdiSend } from '@mdi/js';
import AppAvatar from '@/components/AppAvatar.vue';
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
@@ -25,7 +25,6 @@
const emit = defineEmits(['submit-comment', 'cancel-reply']);
const userProfileStore = useUserProfileStore();
const mediaFileInput = ref(null);
const form = reactive({
body: '',
@@ -73,25 +72,15 @@
form.isInternal = false;
form.mediaFile = null;
form.showMentionPicker = false;
if (mediaFileInput.value) {
mediaFileInput.value.value = '';
}
}
function openMediaPicker() {
mediaFileInput.value?.click();
function selectMediaFile(file) {
form.mediaFile = Array.isArray(file) ? file[0] ?? null : file;
form.showMentionPicker = false;
}
function selectMediaFile(event) {
form.mediaFile = event.target.files?.[0] ?? null;
}
function clearMediaFile() {
form.mediaFile = null;
if (mediaFileInput.value) {
mediaFileInput.value.value = '';
}
}
function toggleMentionPicker() {
@@ -143,11 +132,15 @@
size="md"
/>
<textarea
<v-textarea
v-model="form.body"
class="comment-textarea"
:placeholder="replyTarget ? 'Write a reply...' : 'Write a comment...'"
></textarea>
variant="outlined"
hide-details
auto-grow
rows="3"
/>
</div>
<div
@@ -193,32 +186,27 @@
<div class="comment-composer-toolbar">
<div class="comment-tool-actions">
<label
<div
class="icon-tool-button internal-toggle"
:class="{ active: form.isInternal }"
title="Internal comment"
>
<input
<v-checkbox-btn
v-model="form.isInternal"
type="checkbox"
density="compact"
/>
<v-icon :icon="mdiLockOutline" />
</label>
<button
class="icon-tool-button"
type="button"
</div>
<v-file-input
v-model="form.mediaFile"
class="media-file-control"
title="Upload media from computer"
:class="{ active: form.mediaFile }"
@click="openMediaPicker"
>
<v-icon :icon="mdiImagePlusOutline" />
</button>
<input
ref="mediaFileInput"
class="sr-only"
type="file"
accept="image/png,image/jpeg,image/jpg"
@change="selectMediaFile"
:prepend-icon="mdiImagePlusOutline"
density="compact"
variant="outlined"
hide-details
@update:model-value="selectMediaFile"
/>
<button
class="icon-tool-button"

View File

@@ -787,40 +787,31 @@
</div>
<div class="form-grid">
<label class="field">
<span>Title</span>
<input
v-model="form.title"
type="text"
/>
</label>
<label class="field">
<span>Campaign</span>
<select v-model="form.campaignId">
<option
disabled
value=""
>
Select a campaign
</option>
<option
v-for="campaign in availableCampaigns"
:key="campaign.id"
:value="campaign.id"
>
{{ campaign.name }}
</option>
</select>
</label>
<label class="field">
<span>Due date</span>
<input
v-model="form.dueDate"
type="date"
<v-text-field
v-model="form.title"
label="Title"
variant="outlined"
hide-details
/>
</label>
<v-select
v-model="form.campaignId"
:items="availableCampaigns"
label="Campaign"
item-title="name"
item-value="id"
placeholder="Select a campaign"
variant="outlined"
hide-details
/>
<v-text-field
v-model="form.dueDate"
label="Due date"
type="date"
variant="outlined"
hide-details
/>
<div class="date-context field-wide">
<div class="date-context-days">
@@ -864,19 +855,23 @@
</div>
</div>
<label class="field field-wide">
<span>Change summary</span>
<input
v-model="form.changeSummary"
type="text"
placeholder="What changed in this revision?"
/>
</label>
<v-text-field
v-model="form.changeSummary"
class="field-wide"
label="Change summary"
placeholder="What changed in this revision?"
variant="outlined"
hide-details
/>
<label class="field field-wide">
<span>Shared brief / base caption</span>
<textarea v-model="form.body"></textarea>
</label>
<v-textarea
v-model="form.body"
class="field-wide"
label="Shared brief / base caption"
rows="4"
variant="outlined"
hide-details
/>
<label class="field field-wide">
<span>Shared hashtags</span>
@@ -899,11 +894,13 @@
{{ tag.startsWith('#') ? tag : `#${tag}` }}
</v-chip>
</div>
<input
<v-text-field
v-model="form.hashtags"
type="text"
class="hashtags-inline-input"
placeholder="#launch #campaign #brand"
density="compact"
variant="plain"
hide-details
/>
</div>
</label>
@@ -961,63 +958,61 @@
</div>
<div class="form-grid compact-grid">
<label class="field">
<span>Network</span>
<input
v-model="placement.network"
type="text"
placeholder="Instagram, YouTube..."
/>
</label>
<v-text-field
v-model="placement.network"
label="Network"
placeholder="Instagram, YouTube..."
variant="outlined"
hide-details
/>
<label class="field">
<span>Channel</span>
<select
v-model="placement.channelId"
@change="syncPlacementChannel(placement, placement.channelId)"
>
<option value="">Select a configured channel</option>
<option
v-for="channel in availableChannels"
:key="channel.id"
:value="channel.id"
>
{{ channel.name }}{{ channel.network ? ` · ${channel.network}` : '' }}
</option>
</select>
</label>
<v-select
v-model="placement.channelId"
:items="availableChannels.map(channel => ({
title: `${channel.name}${channel.network ? ` · ${channel.network}` : ''}`,
value: channel.id,
}))"
label="Channel"
placeholder="Select a configured channel"
variant="outlined"
hide-details
clearable
@update:model-value="syncPlacementChannel(placement, $event)"
/>
<label class="field">
<span>Channel name</span>
<input
v-model="placement.channelName"
type="text"
placeholder="IG Feed, YouTube Main..."
/>
</label>
<v-text-field
v-model="placement.channelName"
label="Channel name"
placeholder="IG Feed, YouTube Main..."
variant="outlined"
hide-details
/>
<label class="field">
<span>Variant label</span>
<input
v-model="placement.variantLabel"
type="text"
placeholder="Reel version, Shorts version..."
/>
</label>
<v-text-field
v-model="placement.variantLabel"
label="Variant label"
placeholder="Reel version, Shorts version..."
variant="outlined"
hide-details
/>
<label class="field field-wide">
<span>Channel-specific caption</span>
<textarea v-model="placement.message"></textarea>
</label>
<v-textarea
v-model="placement.message"
class="field-wide"
label="Channel-specific caption"
rows="3"
variant="outlined"
hide-details
/>
<label class="field field-wide">
<span>Channel-specific hashtags</span>
<input
v-model="placement.hashtags"
type="text"
placeholder="#product #launch"
/>
</label>
<v-text-field
v-model="placement.hashtags"
class="field-wide"
label="Channel-specific hashtags"
placeholder="#product #launch"
variant="outlined"
hide-details
/>
</div>
<div class="media-section">
@@ -1042,32 +1037,30 @@
class="media-card"
>
<div class="form-grid compact-grid">
<label class="field">
<span>Media type</span>
<select v-model="media.mediaType">
<option value="Image">Image</option>
<option value="Video">Video</option>
<option value="Document">Document</option>
</select>
</label>
<v-select
v-model="media.mediaType"
:items="['Image', 'Video', 'Document']"
label="Media type"
variant="outlined"
hide-details
/>
<label class="field">
<span>Label</span>
<input
v-model="media.label"
type="text"
placeholder="Cover image, YouTube video..."
/>
</label>
<v-text-field
v-model="media.label"
label="Label"
placeholder="Cover image, YouTube video..."
variant="outlined"
hide-details
/>
<label class="field field-wide">
<span>Media URL / reference</span>
<input
v-model="media.url"
type="text"
placeholder="Google Drive link or asset URL"
/>
</label>
<v-text-field
v-model="media.url"
class="field-wide"
label="Media URL / reference"
placeholder="Google Drive link or asset URL"
variant="outlined"
hide-details
/>
</div>
<button
@@ -1170,46 +1163,43 @@
<template v-else-if="activeProductionTab === 'assets'">
<div class="panel-stack asset-form">
<label class="field">
<span>Type</span>
<select v-model="assetForm.assetType">
<option value="Image">Image</option>
<option value="Video">Video</option>
<option value="Document">Document</option>
<option value="Other">Other</option>
</select>
</label>
<label class="field">
<span>Name</span>
<input
v-model="assetForm.displayName"
type="text"
placeholder="Final reel, cover image..."
/>
</label>
<label class="field field-wide">
<span>Google Drive link</span>
<input
v-model="assetForm.googleDriveLink"
type="url"
placeholder="https://drive.google.com/..."
/>
</label>
<label class="field">
<span>File id</span>
<input
v-model="assetForm.googleDriveFileId"
type="text"
placeholder="Optional if link includes it"
/>
</label>
<label class="field">
<span>Preview URL</span>
<input
v-model="assetForm.previewUrl"
type="url"
/>
</label>
<v-select
v-model="assetForm.assetType"
:items="['Image', 'Video', 'Document', 'Other']"
label="Type"
variant="outlined"
hide-details
/>
<v-text-field
v-model="assetForm.displayName"
label="Name"
placeholder="Final reel, cover image..."
variant="outlined"
hide-details
/>
<v-text-field
v-model="assetForm.googleDriveLink"
class="field-wide"
label="Google Drive link"
type="url"
placeholder="https://drive.google.com/..."
variant="outlined"
hide-details
/>
<v-text-field
v-model="assetForm.googleDriveFileId"
label="File id"
placeholder="Optional if link includes it"
variant="outlined"
hide-details
/>
<v-text-field
v-model="assetForm.previewUrl"
label="Preview URL"
type="url"
variant="outlined"
hide-details
/>
<button
class="primary-button field-wide"
:disabled="detailStore.actions.asset"
@@ -1255,22 +1245,23 @@
</div>
<div class="panel-stack compact-form">
<label class="field field-wide">
<span>New revision reference</span>
<input
v-model="assetRevisionForm(asset.id).sourceReference"
type="url"
placeholder="Updated Drive link or production reference"
/>
</label>
<label class="field field-wide">
<span>Notes</span>
<input
v-model="assetRevisionForm(asset.id).notes"
type="text"
placeholder="What changed?"
/>
</label>
<v-text-field
v-model="assetRevisionForm(asset.id).sourceReference"
class="field-wide"
label="New revision reference"
type="url"
placeholder="Updated Drive link or production reference"
variant="outlined"
hide-details
/>
<v-text-field
v-model="assetRevisionForm(asset.id).notes"
class="field-wide"
label="Notes"
placeholder="What changed?"
variant="outlined"
hide-details
/>
<button
class="secondary-button"
:disabled="detailStore.actions.assetRevision"

View File

@@ -1302,42 +1302,48 @@
</button>
</div>
<div class="scope-row">
<label
<v-radio-group
v-model="customCalendarForm.scope"
class="scope-row"
inline
hide-details
>
<v-radio
v-for="option in addScopeOptions"
:key="option.value"
class="scope-option"
>
<input
v-model="customCalendarForm.scope"
type="radio"
:value="option.value"
>
<span>{{ option.label }}</span>
</label>
</div>
:label="option.label"
:value="option.value"
/>
</v-radio-group>
<div
v-if="activeAddMode === 'catalog'"
class="catalog-panel"
>
<div class="catalog-search">
<input
<v-text-field
v-model="catalogFilters.search"
density="compact"
variant="outlined"
hide-details
type="search"
:placeholder="t('contentItems.calendar.searchCatalog')"
>
<input
/>
<v-text-field
v-model="catalogFilters.country"
type="text"
density="compact"
variant="outlined"
hide-details
maxlength="2"
:placeholder="t('contentItems.calendar.country')"
>
<input
/>
<v-text-field
v-model="catalogFilters.category"
type="text"
density="compact"
variant="outlined"
hide-details
:placeholder="t('contentItems.calendar.category')"
>
/>
<button
class="text-button"
type="button"
@@ -1372,31 +1378,41 @@
</div>
</div>
<form
<v-form
v-else
class="custom-calendar-form"
@submit.prevent="addCustomSource"
>
<input
<v-text-field
v-model="customCalendarForm.title"
type="text"
density="compact"
variant="outlined"
hide-details
:placeholder="t('contentItems.calendar.calendarName')"
>
<input
/>
<v-text-field
v-model="customCalendarForm.sourceUrl"
type="url"
density="compact"
variant="outlined"
hide-details
:placeholder="t('contentItems.calendar.icsUrl')"
>
/>
<div class="custom-form-row">
<input
<v-text-field
v-model="customCalendarForm.color"
type="color"
>
<input
density="compact"
variant="outlined"
hide-details
/>
<v-text-field
v-model="customCalendarForm.category"
type="text"
density="compact"
variant="outlined"
hide-details
:placeholder="t('contentItems.calendar.category')"
>
/>
<button
class="text-button"
type="submit"
@@ -1405,7 +1421,7 @@
<span>{{ t('contentItems.calendar.addCalendar') }}</span>
</button>
</div>
</form>
</v-form>
<p
v-if="addCalendarError || calendarStore.error"