feat: replace all native selects and date inputs with custom components
This commit is contained in:
@@ -6,35 +6,28 @@
|
|||||||
<div class="bg-bg-surface rounded-lg p-4 mb-6 flex flex-wrap items-end gap-4">
|
<div class="bg-bg-surface rounded-lg p-4 mb-6 flex flex-wrap items-end gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Start Date</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Start Date</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="startDate"
|
v-model="startDate"
|
||||||
type="date"
|
placeholder="Start date"
|
||||||
class="px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">End Date</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">End Date</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="endDate"
|
v-model="endDate"
|
||||||
type="date"
|
placeholder="End date"
|
||||||
class="px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="filterProject"
|
v-model="filterProject"
|
||||||
class="px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
:options="projectsStore.projects"
|
||||||
>
|
label-key="name"
|
||||||
<option :value="null">All Projects</option>
|
value-key="id"
|
||||||
<option
|
placeholder="All Projects"
|
||||||
v-for="project in projectsStore.projects"
|
:placeholder-value="null"
|
||||||
:key="project.id"
|
/>
|
||||||
:value="project.id"
|
|
||||||
>
|
|
||||||
{{ project.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="applyFilters"
|
@click="applyFilters"
|
||||||
@@ -139,19 +132,13 @@
|
|||||||
<!-- Project -->
|
<!-- Project -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project *</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project *</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="editForm.project_id"
|
v-model="editForm.project_id"
|
||||||
required
|
:options="projectsStore.projects"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
label-key="name"
|
||||||
>
|
value-key="id"
|
||||||
<option
|
placeholder="Select project"
|
||||||
v-for="project in projectsStore.projects"
|
/>
|
||||||
:key="project.id"
|
|
||||||
:value="project.id"
|
|
||||||
>
|
|
||||||
{{ project.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Description -->
|
<!-- Description -->
|
||||||
@@ -239,6 +226,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { List as ListIcon } from 'lucide-vue-next'
|
import { List as ListIcon } from 'lucide-vue-next'
|
||||||
|
import AppSelect from '../components/AppSelect.vue'
|
||||||
|
import AppDatePicker from '../components/AppDatePicker.vue'
|
||||||
import { useEntriesStore, type TimeEntry } from '../stores/entries'
|
import { useEntriesStore, type TimeEntry } from '../stores/entries'
|
||||||
import { useProjectsStore } from '../stores/projects'
|
import { useProjectsStore } from '../stores/projects'
|
||||||
|
|
||||||
|
|||||||
@@ -126,39 +126,30 @@
|
|||||||
<!-- Client -->
|
<!-- Client -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Client *</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Client *</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="createForm.client_id"
|
v-model="createForm.client_id"
|
||||||
required
|
:options="clientsStore.clients"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
label-key="name"
|
||||||
>
|
value-key="id"
|
||||||
<option :value="0">Select a client</option>
|
placeholder="Select a client"
|
||||||
<option
|
:placeholder-value="0"
|
||||||
v-for="client in clientsStore.clients"
|
/>
|
||||||
:key="client.id"
|
|
||||||
:value="client.id"
|
|
||||||
>
|
|
||||||
{{ client.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Date -->
|
<!-- Date -->
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Invoice Date *</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Invoice Date *</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="createForm.date"
|
v-model="createForm.date"
|
||||||
type="date"
|
placeholder="Invoice date"
|
||||||
required
|
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Due Date</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Due Date</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="createForm.due_date"
|
v-model="createForm.due_date"
|
||||||
type="date"
|
placeholder="Due date"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,6 +332,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { FileText } from 'lucide-vue-next'
|
import { FileText } from 'lucide-vue-next'
|
||||||
|
import AppSelect from '../components/AppSelect.vue'
|
||||||
|
import AppDatePicker from '../components/AppDatePicker.vue'
|
||||||
import { useInvoicesStore, type Invoice } from '../stores/invoices'
|
import { useInvoicesStore, type Invoice } from '../stores/invoices'
|
||||||
import { useClientsStore } from '../stores/clients'
|
import { useClientsStore } from '../stores/clients'
|
||||||
import { useToastStore } from '../stores/toast'
|
import { useToastStore } from '../stores/toast'
|
||||||
|
|||||||
@@ -92,19 +92,14 @@
|
|||||||
<!-- Client -->
|
<!-- Client -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Client</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Client</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="formData.client_id"
|
v-model="formData.client_id"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
:options="clientsStore.clients"
|
||||||
>
|
label-key="name"
|
||||||
<option :value="undefined">No client</option>
|
value-key="id"
|
||||||
<option
|
placeholder="No client"
|
||||||
v-for="client in clientsStore.clients"
|
:placeholder-value="undefined"
|
||||||
:key="client.id"
|
/>
|
||||||
:value="client.id"
|
|
||||||
>
|
|
||||||
{{ client.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hourly Rate -->
|
<!-- Hourly Rate -->
|
||||||
@@ -201,6 +196,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { FolderKanban } from 'lucide-vue-next'
|
import { FolderKanban } from 'lucide-vue-next'
|
||||||
|
import AppSelect from '../components/AppSelect.vue'
|
||||||
import { useProjectsStore, type Project } from '../stores/projects'
|
import { useProjectsStore, type Project } from '../stores/projects'
|
||||||
import { useClientsStore } from '../stores/clients'
|
import { useClientsStore } from '../stores/clients'
|
||||||
|
|
||||||
|
|||||||
@@ -6,18 +6,16 @@
|
|||||||
<div class="bg-bg-surface rounded-lg p-4 mb-6 flex flex-wrap items-end gap-4">
|
<div class="bg-bg-surface rounded-lg p-4 mb-6 flex flex-wrap items-end gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Start Date</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Start Date</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="startDate"
|
v-model="startDate"
|
||||||
type="date"
|
placeholder="Start date"
|
||||||
class="px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">End Date</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">End Date</label>
|
||||||
<input
|
<AppDatePicker
|
||||||
v-model="endDate"
|
v-model="endDate"
|
||||||
type="date"
|
placeholder="End date"
|
||||||
class="px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -109,6 +107,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { Bar } from 'vue-chartjs'
|
import { Bar } from 'vue-chartjs'
|
||||||
import { BarChart3 } from 'lucide-vue-next'
|
import { BarChart3 } from 'lucide-vue-next'
|
||||||
|
import AppDatePicker from '../components/AppDatePicker.vue'
|
||||||
import { useToastStore } from '../stores/toast'
|
import { useToastStore } from '../stores/toast'
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
|
|||||||
@@ -26,37 +26,27 @@
|
|||||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Project</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="selectedProject"
|
v-model="selectedProject"
|
||||||
|
:options="activeProjects"
|
||||||
|
label-key="name"
|
||||||
|
value-key="id"
|
||||||
|
placeholder="Select project"
|
||||||
|
:placeholder-value="null"
|
||||||
:disabled="timerStore.isRunning"
|
:disabled="timerStore.isRunning"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible disabled:opacity-40 disabled:cursor-not-allowed"
|
/>
|
||||||
>
|
|
||||||
<option :value="null">Select project</option>
|
|
||||||
<option
|
|
||||||
v-for="project in activeProjects"
|
|
||||||
:key="project.id"
|
|
||||||
:value="project.id"
|
|
||||||
>
|
|
||||||
{{ project.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Task</label>
|
<label class="block text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1.5">Task</label>
|
||||||
<select
|
<AppSelect
|
||||||
v-model="selectedTask"
|
v-model="selectedTask"
|
||||||
|
:options="projectTasks"
|
||||||
|
label-key="name"
|
||||||
|
value-key="id"
|
||||||
|
placeholder="Select task"
|
||||||
|
:placeholder-value="null"
|
||||||
:disabled="timerStore.isRunning || !selectedProject"
|
:disabled="timerStore.isRunning || !selectedProject"
|
||||||
class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.8125rem] text-text-primary focus:outline-none focus:border-border-visible disabled:opacity-40 disabled:cursor-not-allowed"
|
/>
|
||||||
>
|
|
||||||
<option :value="null">Select task</option>
|
|
||||||
<option
|
|
||||||
v-for="task in projectTasks"
|
|
||||||
:key="task.id"
|
|
||||||
:value="task.id"
|
|
||||||
>
|
|
||||||
{{ task.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -119,6 +109,7 @@ import { useProjectsStore, type Task } from '../stores/projects'
|
|||||||
import { useEntriesStore } from '../stores/entries'
|
import { useEntriesStore } from '../stores/entries'
|
||||||
import { useToastStore } from '../stores/toast'
|
import { useToastStore } from '../stores/toast'
|
||||||
import { Timer as TimerIcon } from 'lucide-vue-next'
|
import { Timer as TimerIcon } from 'lucide-vue-next'
|
||||||
|
import AppSelect from '../components/AppSelect.vue'
|
||||||
|
|
||||||
const timerStore = useTimerStore()
|
const timerStore = useTimerStore()
|
||||||
const projectsStore = useProjectsStore()
|
const projectsStore = useProjectsStore()
|
||||||
|
|||||||
Reference in New Issue
Block a user