fix(ui): unify admin table toolbar layout with search and buttons in single row
Standardize filter bar layout across admin pages to place search/filters on left and action buttons on right within the same row, improving visual consistency and space utilization.
This commit is contained in:
@@ -1,26 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TablePageLayout>
|
<TablePageLayout>
|
||||||
<template #actions>
|
|
||||||
<div class="flex justify-end gap-3">
|
|
||||||
<button
|
|
||||||
@click="loadAnnouncements"
|
|
||||||
:disabled="loading"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
:title="t('common.refresh')"
|
|
||||||
>
|
|
||||||
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
|
||||||
</button>
|
|
||||||
<button @click="openCreateDialog" class="btn btn-primary">
|
|
||||||
<Icon name="plus" size="md" class="mr-1" />
|
|
||||||
{{ t('admin.announcements.createAnnouncement') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #filters>
|
<template #filters>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<div class="max-w-md flex-1">
|
<!-- Left: Search + Filters -->
|
||||||
|
<div class="flex-1 sm:max-w-64">
|
||||||
<input
|
<input
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -29,13 +13,27 @@
|
|||||||
@input="handleSearch"
|
@input="handleSearch"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<Select
|
||||||
<Select
|
v-model="filters.status"
|
||||||
v-model="filters.status"
|
:options="statusFilterOptions"
|
||||||
:options="statusFilterOptions"
|
class="w-40"
|
||||||
class="w-40"
|
@change="handleStatusChange"
|
||||||
@change="handleStatusChange"
|
/>
|
||||||
/>
|
|
||||||
|
<!-- Right: Action buttons -->
|
||||||
|
<div class="flex flex-1 flex-wrap items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
@click="loadAnnouncements"
|
||||||
|
:disabled="loading"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
:title="t('common.refresh')"
|
||||||
|
>
|
||||||
|
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
||||||
|
</button>
|
||||||
|
<button @click="openCreateDialog" class="btn btn-primary">
|
||||||
|
<Icon name="plus" size="md" class="mr-1" />
|
||||||
|
{{ t('admin.announcements.createAnnouncement') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TablePageLayout>
|
<TablePageLayout>
|
||||||
<template #actions>
|
|
||||||
<div class="flex justify-end gap-3">
|
|
||||||
<button
|
|
||||||
@click="loadCodes"
|
|
||||||
:disabled="loading"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
:title="t('common.refresh')"
|
|
||||||
>
|
|
||||||
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
|
||||||
</button>
|
|
||||||
<button @click="showCreateDialog = true" class="btn btn-primary">
|
|
||||||
<Icon name="plus" size="md" class="mr-1" />
|
|
||||||
{{ t('admin.promo.createCode') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #filters>
|
<template #filters>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<div class="max-w-md flex-1">
|
<!-- Left: Search + Filters -->
|
||||||
|
<div class="flex-1 sm:max-w-64">
|
||||||
<input
|
<input
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -29,13 +13,27 @@
|
|||||||
@input="handleSearch"
|
@input="handleSearch"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<Select
|
||||||
<Select
|
v-model="filters.status"
|
||||||
v-model="filters.status"
|
:options="filterStatusOptions"
|
||||||
:options="filterStatusOptions"
|
class="w-36"
|
||||||
class="w-36"
|
@change="loadCodes"
|
||||||
@change="loadCodes"
|
/>
|
||||||
/>
|
|
||||||
|
<!-- Right: Action buttons -->
|
||||||
|
<div class="flex flex-1 flex-wrap items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
@click="loadCodes"
|
||||||
|
:disabled="loading"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
:title="t('common.refresh')"
|
||||||
|
>
|
||||||
|
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
||||||
|
</button>
|
||||||
|
<button @click="showCreateDialog = true" class="btn btn-primary">
|
||||||
|
<Icon name="plus" size="md" class="mr-1" />
|
||||||
|
{{ t('admin.promo.createCode') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,9 +2,42 @@
|
|||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TablePageLayout>
|
<TablePageLayout>
|
||||||
<template #filters>
|
<template #filters>
|
||||||
<div class="space-y-3">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<!-- Row 1: Actions -->
|
<!-- Left: Search + Filters -->
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div class="relative w-full sm:w-64">
|
||||||
|
<Icon
|
||||||
|
name="search"
|
||||||
|
size="md"
|
||||||
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="text"
|
||||||
|
:placeholder="t('admin.proxies.searchProxies')"
|
||||||
|
class="input pl-10"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full sm:w-40">
|
||||||
|
<Select
|
||||||
|
v-model="filters.protocol"
|
||||||
|
:options="protocolOptions"
|
||||||
|
:placeholder="t('admin.proxies.allProtocols')"
|
||||||
|
@change="loadProxies"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-full sm:w-36">
|
||||||
|
<Select
|
||||||
|
v-model="filters.status"
|
||||||
|
:options="statusOptions"
|
||||||
|
:placeholder="t('admin.proxies.allStatus')"
|
||||||
|
@change="loadProxies"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right: All action buttons -->
|
||||||
|
<div class="flex flex-1 flex-wrap items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
@click="loadProxies"
|
@click="loadProxies"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
@@ -42,41 +75,6 @@
|
|||||||
{{ t('admin.proxies.createProxy') }}
|
{{ t('admin.proxies.createProxy') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 2: Search + Filters -->
|
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
|
||||||
<div class="relative w-full sm:w-64">
|
|
||||||
<Icon
|
|
||||||
name="search"
|
|
||||||
size="md"
|
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
v-model="searchQuery"
|
|
||||||
type="text"
|
|
||||||
:placeholder="t('admin.proxies.searchProxies')"
|
|
||||||
class="input pl-10"
|
|
||||||
@input="handleSearch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-full sm:w-40">
|
|
||||||
<Select
|
|
||||||
v-model="filters.protocol"
|
|
||||||
:options="protocolOptions"
|
|
||||||
:placeholder="t('admin.proxies.allProtocols')"
|
|
||||||
@change="loadProxies"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-36">
|
|
||||||
<Select
|
|
||||||
v-model="filters.status"
|
|
||||||
:options="statusOptions"
|
|
||||||
:placeholder="t('admin.proxies.allStatus')"
|
|
||||||
@change="loadProxies"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TablePageLayout>
|
<TablePageLayout>
|
||||||
<template #actions>
|
|
||||||
<div class="flex justify-end gap-3">
|
|
||||||
<button
|
|
||||||
@click="loadCodes"
|
|
||||||
:disabled="loading"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
:title="t('common.refresh')"
|
|
||||||
>
|
|
||||||
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
|
||||||
</button>
|
|
||||||
<button @click="showGenerateDialog = true" class="btn btn-primary">
|
|
||||||
{{ t('admin.redeem.generateCodes') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #filters>
|
<template #filters>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<div class="max-w-md flex-1">
|
<!-- Left: Search + Filters -->
|
||||||
<input
|
<div class="flex-1 sm:max-w-64">
|
||||||
v-model="searchQuery"
|
<input
|
||||||
type="text"
|
v-model="searchQuery"
|
||||||
:placeholder="t('admin.redeem.searchCodes')"
|
type="text"
|
||||||
class="input"
|
:placeholder="t('admin.redeem.searchCodes')"
|
||||||
@input="handleSearch"
|
class="input"
|
||||||
/>
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
|
||||||
<Select
|
<Select
|
||||||
v-model="filters.type"
|
v-model="filters.type"
|
||||||
:options="filterTypeOptions"
|
:options="filterTypeOptions"
|
||||||
@@ -41,9 +25,23 @@
|
|||||||
class="w-36"
|
class="w-36"
|
||||||
@change="loadCodes"
|
@change="loadCodes"
|
||||||
/>
|
/>
|
||||||
<button @click="handleExportCodes" class="btn btn-secondary">
|
|
||||||
{{ t('admin.redeem.exportCsv') }}
|
<!-- Right: Action buttons -->
|
||||||
</button>
|
<div class="flex flex-1 flex-wrap items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
@click="loadCodes"
|
||||||
|
:disabled="loading"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
:title="t('common.refresh')"
|
||||||
|
>
|
||||||
|
<Icon name="refresh" size="md" :class="loading ? 'animate-spin' : ''" />
|
||||||
|
</button>
|
||||||
|
<button @click="handleExportCodes" class="btn btn-secondary">
|
||||||
|
{{ t('admin.redeem.exportCsv') }}
|
||||||
|
</button>
|
||||||
|
<button @click="showGenerateDialog = true" class="btn btn-primary">
|
||||||
|
{{ t('admin.redeem.generateCodes') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<TablePageLayout>
|
<TablePageLayout>
|
||||||
<!-- Single Row: Search, Filters, and Actions -->
|
<!-- Single Row: Search, Filters, and Actions -->
|
||||||
<template #filters>
|
<template #filters>
|
||||||
<div class="flex w-full flex-col gap-3 md:flex-row md:flex-wrap-reverse md:items-center md:justify-between md:gap-4">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<!-- Left: Search + Active Filters -->
|
<!-- Left: Search + Active Filters -->
|
||||||
<div class="flex min-w-[280px] flex-1 flex-wrap content-start items-center gap-3 md:order-1">
|
<div class="flex flex-1 flex-wrap items-center gap-3">
|
||||||
<!-- Search Box -->
|
<!-- Search Box -->
|
||||||
<div class="relative w-full md:w-64">
|
<div class="relative w-full md:w-64">
|
||||||
<Icon
|
<Icon
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right: Actions and Settings -->
|
<!-- Right: Actions and Settings -->
|
||||||
<div class="flex w-full items-center justify-between gap-2 md:order-2 md:ml-auto md:max-w-full md:flex-wrap md:justify-end md:gap-3">
|
<div class="flex flex-wrap items-center justify-end gap-2">
|
||||||
<!-- Mobile: Secondary buttons (icon only) -->
|
<!-- Mobile: Secondary buttons (icon only) -->
|
||||||
<div class="flex items-center gap-2 md:contents">
|
<div class="flex items-center gap-2 md:contents">
|
||||||
<!-- Refresh Button -->
|
<!-- Refresh Button -->
|
||||||
|
|||||||
Reference in New Issue
Block a user