<template>
    <div class="default-main game-rtp-page">
        <section class="setting-grid">
            <div class="panel setting-panel">
                <div class="panel-title">{{ t('embed.gameRtp.settingTitle') }}</div>
                <div class="panel-body setting-form">
                    <div class="form-row">
                        <label>{{ t('embed.gameRtp.autoRtp') }}:</label>
                        <el-select v-model="settings.autoRtp">
                            <el-option label="OFF" :value="0" />
                            <el-option label="ON" :value="1" />
                        </el-select>
                    </div>
                    <div class="form-row">
                        <label>{{ t('embed.gameRtp.autoAmount') }}:</label>
                        <el-input v-model="settings.autoAmount" placeholder='e.g. {"max":70,"min":20}' />
                    </div>
                    <div class="form-row">
                        <label>{{ t('embed.gameRtp.frequency') }}:</label>
                        <el-input v-model="settings.frequency" placeholder="e.g. 60" />
                    </div>
                    <div class="form-hint">
                        {{ t('embed.gameRtp.lastRefresh') }}: {{ lastRefresh }}
                    </div>
                    <div class="form-row">
                        <label>{{ t('embed.gameRtp.providerDisplay') }}:</label>
                        <el-input v-model="settings.providerDisplay" placeholder='e.g. ["XE88","LFC2","GMSL1"]' />
                    </div>
                    <div class="form-hint">{{ t('embed.gameRtp.providerHint') }}</div>
                    <div class="form-row textarea-row">
                        <label>{{ t('embed.gameRtp.customConfig') }}:</label>
                        <el-input
                            v-model="settings.customConfig"
                            type="textarea"
                            :rows="7"
                            :placeholder="configTemplate"
                            resize="vertical"
                        />
                    </div>
                    <div class="form-row">
                        <label></label>
                        <el-button size="small" @click="loadTemplate">{{ t('embed.gameRtp.loadTemplate') }}</el-button>
                    </div>
                    <div class="form-row">
                        <label>{{ t('embed.gameRtp.headerImage') }}:</label>
                        <el-input v-model="settings.headerImage" placeholder="e.g. https://image.png" />
                    </div>
                    <div class="color-grid">
                        <div v-for="item in colorFields" :key="item.key" class="color-item">
                            <label>{{ t(item.label) }}:</label>
                            <el-color-picker v-model="settings[item.key]" />
                            <span>{{ settings[item.key] }}</span>
                        </div>
                    </div>
                    <el-button type="primary" class="save-button" :loading="savingSettings" @click="saveSettings">
                        {{ t('embed.gameRtp.save') }}
                    </el-button>
                </div>
            </div>

            <div class="panel embed-panel">
                <div class="panel-title">{{ t('embed.gameRtp.embedCode') }}</div>
                <div class="panel-body">
                    <el-input v-model="embedCode" type="textarea" :rows="23" readonly resize="none" />
                    <el-button class="copy-button" @click="copyEmbedCode">{{ t('embed.gameRtp.copyCode') }}</el-button>
                </div>
            </div>
        </section>

        <section class="panel data-panel">
            <div class="panel-title">{{ t('embed.gameRtp.dataTitle') }}</div>
            <div class="filter-bar">
                <div class="filter-field">
                    <label>{{ t('embed.gameRtp.provider') }}:</label>
                    <el-select v-model="filters.provider" filterable>
                        <el-option :label="t('embed.gameRtp.allProviders')" value="" />
                        <el-option v-for="provider in providers" :key="provider" :label="provider" :value="provider" />
                    </el-select>
                </div>
                <div class="filter-field">
                    <label>{{ t('embed.gameRtp.gameName') }}:</label>
                    <el-input v-model="filters.name" :placeholder="t('embed.gameRtp.searchName')" clearable @keyup.enter="search" />
                </div>
                <div class="filter-actions">
                    <el-button type="primary" @click="search">{{ t('embed.gameRtp.search') }}</el-button>
                    <el-button @click="clearSearch">{{ t('embed.gameRtp.clear') }}</el-button>
                </div>
            </div>

            <div v-loading="loading" class="table-wrap">
                <el-table :data="rows" border stripe>
                    <el-table-column prop="id" :label="t('embed.gameRtp.gameId')" width="100" />
                    <el-table-column prop="provider" :label="t('embed.gameRtp.provider')" min-width="140" />
                    <el-table-column prop="name" :label="t('embed.gameRtp.gameName')" min-width="180" />
                    <el-table-column :label="t('embed.gameRtp.imageUrl')" min-width="360">
                        <template #default="{ row }">
                            <a class="image-link" :href="row.imageUrl" target="_blank" rel="noopener noreferrer">{{ row.imageUrl }}</a>
                        </template>
                    </el-table-column>
                    <el-table-column prop="rtp" :label="t('embed.gameRtp.rtp')" width="100" align="center" />
                    <el-table-column :label="t('embed.gameRtp.status')" width="110" align="center">
                        <template #default="{ row }">
                            <el-tag :type="row.status === 1 ? 'success' : 'info'" effect="plain">
                                {{ row.status === 1 ? t('embed.gameRtp.active') : t('embed.gameRtp.inactive') }}
                            </el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column :label="t('embed.gameRtp.action')" width="150" fixed="right">
                        <template #default="{ row }">
                            <el-button link type="primary" @click="openDialog('edit', row)">{{ t('embed.gameRtp.edit') }}</el-button>
                            <el-button link type="danger" @click="removeRow(row)">{{ t('embed.gameRtp.delete') }}</el-button>
                        </template>
                    </el-table-column>
                </el-table>
            </div>

            <div class="table-footer">
                <span>{{ t('embed.gameRtp.totalRecords', { total }) }}</span>
                <el-pagination
                    v-model:current-page="currentPage"
                    background
                    layout="prev, pager, next"
                    :page-count="lastPage"
                    @current-change="loadGameRtp"
                />
            </div>
        </section>

        <div class="bottom-actions">
            <el-button type="success" @click="openDialog('create')">{{ t('embed.gameRtp.create') }}</el-button>
            <el-button type="warning" @click="importGames">{{ t('embed.gameRtp.import') }}</el-button>
        </div>

        <el-dialog
            v-model="dialogVisible"
            :title="dialogMode === 'create' ? t('embed.gameRtp.createTitle') : t('embed.gameRtp.editTitle')"
            width="520px"
            destroy-on-close
        >
            <el-form ref="formRef" :model="form" :rules="rules" label-position="top">
                <el-form-item :label="t('embed.gameRtp.provider')" prop="provider">
                    <el-input v-model="form.provider" placeholder="e.g. XE88" />
                </el-form-item>
                <el-form-item :label="t('embed.gameRtp.gameName')" prop="name">
                    <el-input v-model="form.name" placeholder="e.g. Aztec" />
                </el-form-item>
                <el-form-item :label="t('embed.gameRtp.imageUrl')" prop="imageUrl">
                    <el-input v-model="form.imageUrl" placeholder="e.g. https://image.png" />
                </el-form-item>
                <el-form-item :label="t('embed.gameRtp.rtp')" prop="rtp">
                    <el-input-number v-model="form.rtp" :min="0" :max="100" />
                </el-form-item>
                <el-form-item :label="t('embed.gameRtp.status')" prop="status">
                    <el-select v-model="form.status">
                        <el-option :label="t('embed.gameRtp.active')" :value="1" />
                        <el-option :label="t('embed.gameRtp.inactive')" :value="0" />
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="dialogVisible = false">{{ t('embed.gameRtp.cancel') }}</el-button>
                <el-button type="primary" :loading="submitting" @click="submitForm">{{ t('embed.gameRtp.confirm') }}</el-button>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, onMounted, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { addGame, del, editGame, gameRtp, saveGameRtp } from '/@/api/backend/embed'

defineOptions({
    name: 'embed/gameRtp',
})

interface GameRtpRow {
    id: number
    provider: string
    name: string
    imageUrl: string
    rtp: number | null
    status: number
}

interface GameRtpSourceRow {
    id?: number | string
    game_id?: number | string
    provider?: string
    game_provider?: string
    provider_site?: string
    type?: string
    name?: string
    game_name?: string
    image_url?: string
    game_image_url?: string
    url?: string
    rtp?: number | string
    game_rtp?: number | string
    status?: number | string
    game_status?: number | string
}

interface GameRtpSetting {
    auto_game_rtp?: number | string
    auto_rtp_amount?: string | Record<string, string | number> | null
    auto_frequency?: number | string
    provider_display?: string | Record<string, unknown> | unknown[] | null
    custom_config?: string | Record<string, unknown> | null
    header_image_url?: string | null
    text_color?: string | null
    button_text_color?: string | null
    base_color?: string | null
    button_bg_color?: string | null
    outline_color?: string | null
    progress_bar_bg_color?: string | null
    update_time?: number | string | null
}

type ColorSettingKey = 'textColor' | 'baseColor' | 'outlineColor' | 'buttonTextColor' | 'buttonBgColor' | 'progressBgColor'

const { t } = useI18n()
const configTemplate = `{
    "customGameRTP": [
        {"max": 99, "min": 95, "gameId": 153},
        {"max": 80, "min": 60, "gameId": 723}
    ],
    "providerNameMapping": {
        "VP": "V-POWER",
        "MG888H52": "MEGA888 H5"
    }
}`

const settings = reactive<Record<ColorSettingKey, string> & {
    autoRtp: number
    autoAmount: string
    frequency: string
    providerDisplay: string
    customConfig: string
    headerImage: string
}>({
    autoRtp: 1,
    autoAmount: '{"max":"90","min":"30"}',
    frequency: '10',
    providerDisplay: '',
    customConfig: '',
    headerImage: '',
    textColor: '#ffffff',
    baseColor: '#045bb9',
    outlineColor: '#000000',
    buttonTextColor: '#ffffff',
    buttonBgColor: '#1c202a',
    progressBgColor: '#5f5f5f',
})

const colorFields: { key: ColorSettingKey; label: string }[] = [
    { key: 'textColor', label: 'embed.gameRtp.textColor' },
    { key: 'baseColor', label: 'embed.gameRtp.baseColor' },
    { key: 'outlineColor', label: 'embed.gameRtp.outlineColor' },
    { key: 'buttonTextColor', label: 'embed.gameRtp.buttonTextColor' },
    { key: 'buttonBgColor', label: 'embed.gameRtp.buttonBgColor' },
    { key: 'progressBgColor', label: 'embed.gameRtp.progressBgColor' },
]

const lastRefresh = ref('')
const embedCode = ref(`<div style="position: relative;max-width: 1000px;margin: 0 auto;overflow: hidden;border: none;">
    <iframe id="gameRtpIframe" src="https://api.tl-tech.top/index.php/api/external/gameRtp" style="width: 100%;overflow: hidden;border: none;"></iframe>
    <script>
        (() => {
            const merchantId = (typeof _ !== "undefined" && typeof _.getVar === "function") ? (_.getVar("merchantId") ?? "") : "";
            if (!merchantId) {
                console.log("merchantId not found");
                return;
            }
            function init(iframe) {
                iframe.addEventListener('load', function() {
                    iframe.contentWindow.postMessage({ type: 'setIframeData', clientMerchant: merchantId }, '*');
                });
                window.addEventListener("message", function(event) {
                    if (event.data && event.data.type === "setGameRtpIframeHeight") {
                        iframe.style.height = event.data.height + 50 + "px";
                    }
                }, false);
            }
            var el = document.getElementById('gameRtpIframe');
            if (el) { init(el); return; }
            var attempts = 0;
            var timer = setInterval(function() {
                attempts++;
                var el = document.getElementById('gameRtpIframe');
                if (el) {
                    clearInterval(timer);
                    init(el);
                } else if (attempts >= 5) {
                    clearInterval(timer);
                    alert("iframe not found");
                }
            }, 1000);
        })();
    /script>
</div>`)

const rows = ref<GameRtpRow[]>([])
const filters = reactive({ provider: '', name: '' })
const appliedFilters = reactive({ provider: '', name: '' })
const providerOptions = ref<string[]>([])
const providers = computed(() => [...new Set([...providerOptions.value, ...rows.value.map((row) => row.provider)].filter(Boolean))].sort())
const currentPage = ref(1)
const lastPage = ref(1)
const pageSize = ref(50)
const total = ref(0)
const loading = ref(false)

const toNumber = (value: unknown) => {
    const number = Number(value)
    return Number.isFinite(number) ? number : 0
}

const normalizeStatus = (value: unknown) => {
    if (typeof value === 'string') {
        const normalized = value.trim().toUpperCase()
        if (normalized === 'ACTIVE') return 1
        if (normalized === 'INACTIVE') return 0
    }
    return toNumber(value) === 1 ? 1 : 0
}

const formatUpdateTime = (value: unknown) => {
    if (!value) return ''
    if (typeof value === 'string' && !/^\d+$/.test(value)) return value
    const timestamp = toNumber(value)
    if (!timestamp) return ''
    return new Date(timestamp * (timestamp < 1e12 ? 1000 : 1)).toLocaleString()
}

const formatJsonSetting = (value: string | Record<string, unknown> | unknown[] | null | undefined) => {
    if (!value) return ''
    return typeof value === 'string' ? value : JSON.stringify(value)
}

const applyRtpSetting = (setting?: GameRtpSetting) => {
    if (!setting) return
    settings.autoRtp = toNumber(setting.auto_game_rtp) === 1 ? 1 : 0
    settings.autoAmount = formatJsonSetting(setting.auto_rtp_amount)
    settings.frequency = String(setting.auto_frequency ?? '')
    settings.providerDisplay = formatJsonSetting(setting.provider_display)
    settings.customConfig = formatJsonSetting(setting.custom_config)
    settings.headerImage = setting.header_image_url ?? ''
    settings.textColor = setting.text_color ?? ''
    settings.buttonTextColor = setting.button_text_color ?? ''
    settings.baseColor = setting.base_color ?? ''
    settings.buttonBgColor = setting.button_bg_color ?? ''
    settings.outlineColor = setting.outline_color ?? ''
    settings.progressBgColor = setting.progress_bar_bg_color ?? ''
    lastRefresh.value = formatUpdateTime(setting.update_time)
}

const normalizeResponse = (responseData: any) => {
    const source = responseData?.data ?? responseData ?? {}
    const list = Array.isArray(source.list) ? source.list : []

    const normalizedRows: GameRtpRow[] = list.map((row: GameRtpSourceRow) => ({
        id: toNumber(row.id ?? row.game_id),
        provider: String(row.provider_site ?? row.game_provider ?? row.provider ?? row.type ?? ''),
        name: String(row.game_name ?? row.name ?? ''),
        imageUrl: String(row.game_image_url ?? row.image_url ?? row.url ?? ''),
        rtp: row.game_rtp == null && row.rtp == null ? null : toNumber(row.game_rtp ?? row.rtp),
        status: normalizeStatus(row.game_status ?? row.status),
    }))

    if (currentPage.value === 1 && normalizedRows.length) pageSize.value = normalizedRows.length
    const normalizedTotal = toNumber(source.total ?? normalizedRows.length)

    return {
        rows: normalizedRows,
        total: normalizedTotal,
        lastPage: Math.max(1, Math.ceil(normalizedTotal / pageSize.value)),
        providers: Array.isArray(source.provider) ? source.provider.map(String) : [],
        setting: source.rtp_setting as GameRtpSetting | undefined,
    }
}

const loadGameRtp = async () => {
    loading.value = true
    try {
        const response = await gameRtp({
            provider: appliedFilters.provider,
            game_name: appliedFilters.name,
            page: currentPage.value,
        })
        const normalized = normalizeResponse(response.data)
        rows.value = normalized.rows
        total.value = normalized.total
        lastPage.value = normalized.lastPage
        providerOptions.value = normalized.providers
        applyRtpSetting(normalized.setting)
    } finally {
        loading.value = false
    }
}

const search = async () => {
    Object.assign(appliedFilters, filters)
    currentPage.value = 1
    await loadGameRtp()
}

const clearSearch = async () => {
    Object.assign(filters, { provider: '', name: '' })
    await search()
}

const loadTemplate = () => {
    settings.customConfig = configTemplate
}

const savingSettings = ref(false)

const saveSettings = async () => {
    let autoRtpAmount: Record<string, string | number>
    let providerDisplay: Record<string, unknown> | unknown[] = []
    let customConfig: Record<string, unknown> = {}
    try {
        const parsed = JSON.parse(settings.autoAmount)
        if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') throw new Error()
        autoRtpAmount = parsed
    } catch {
        ElMessage.error(t('embed.gameRtp.invalidAutoAmount'))
        return
    }

    try {
        if (settings.providerDisplay.trim()) {
            const parsed = JSON.parse(settings.providerDisplay)
            if (!parsed || typeof parsed !== 'object') throw new Error()
            providerDisplay = parsed
        }
    } catch {
        ElMessage.error(t('embed.gameRtp.invalidProviderDisplay'))
        return
    }

    try {
        if (settings.customConfig.trim()) {
            const parsed = JSON.parse(settings.customConfig)
            if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') throw new Error()
            customConfig = parsed
        }
    } catch {
        ElMessage.error(t('embed.gameRtp.invalidCustomConfig'))
        return
    }

    savingSettings.value = true
    try {
        await saveGameRtp({
            auto_game_rtp: settings.autoRtp as 0 | 1,
            auto_rtp_amount: autoRtpAmount,
            auto_frequency: toNumber(settings.frequency),
            provider_display: providerDisplay,
            custom_config: customConfig,
            header_image_url: settings.headerImage,
            text_color: settings.textColor,
            button_text_color: settings.buttonTextColor,
            base_color: settings.baseColor,
            button_bg_color: settings.buttonBgColor,
            outline_color: settings.outlineColor,
            progress_bar_bg_color: settings.progressBgColor,
        })
        await loadGameRtp()
    } finally {
        savingSettings.value = false
    }
}

const copyEmbedCode = async () => {
    try {
        await navigator.clipboard.writeText(embedCode.value)
        ElMessage.success(t('embed.gameRtp.copied'))
    } catch {
        ElMessage.error(t('embed.gameRtp.copyFailed'))
    }
}

const dialogVisible = ref(false)
const dialogMode = ref<'create' | 'edit'>('create')
const editingId = ref<number>()
const submitting = ref(false)
const formRef = ref<FormInstance>()
const form = reactive({
    provider: '',
    name: '',
    imageUrl: '',
    rtp: 0,
    status: 1,
})
const rules = computed<FormRules>(() => ({
    provider: [{ required: true, message: t('embed.gameRtp.providerRequired'), trigger: 'blur' }],
    name: [{ required: true, message: t('embed.gameRtp.nameRequired'), trigger: 'blur' }],
    imageUrl: [{ required: true, message: t('embed.gameRtp.urlRequired'), trigger: 'blur' }],
}))

const openDialog = (mode: 'create' | 'edit', row?: GameRtpRow) => {
    dialogMode.value = mode
    editingId.value = row?.id
    Object.assign(form, row ?? { provider: '', name: '', imageUrl: '', rtp: 0, status: 1 })
    dialogVisible.value = true
}

const submitForm = async () => {
    if (!(await formRef.value?.validate().catch(() => false))) return

    if (dialogMode.value === 'create') {
        submitting.value = true
        try {
            await addGame({
                provider_site: form.provider,
                game_name: form.name,
                image_url: form.imageUrl,
                rtp: form.rtp,
                status: form.status as 0 | 1,
            })
            dialogVisible.value = false
            currentPage.value = 1
            await loadGameRtp()
        } finally {
            submitting.value = false
        }
    } else {
        if (editingId.value == null) return
        submitting.value = true
        try {
            await editGame({
                id: editingId.value,
                provider_site: form.provider,
                game_name: form.name,
                image_url: form.imageUrl,
                rtp: form.rtp,
                status: form.status as 0 | 1,
            })
            dialogVisible.value = false
            await loadGameRtp()
        } finally {
            submitting.value = false
        }
    }
}

const removeRow = async (row: GameRtpRow) => {
    try {
        await ElMessageBox.confirm(t('embed.gameRtp.deleteConfirm', { name: row.name }), t('embed.gameRtp.warning'), {
            type: 'warning',
            confirmButtonText: t('embed.gameRtp.confirm'),
            cancelButtonText: t('embed.gameRtp.cancel'),
        })
        await del({ ids: [row.id] })
        if (rows.value.length === 1 && currentPage.value > 1) currentPage.value -= 1
        await loadGameRtp()
    } catch {
        return
    }
}

const importGames = async () => {
    try {
        await ElMessageBox.confirm(t('embed.gameRtp.importConfirm'), t('embed.gameRtp.warning'), {
            type: 'warning',
            confirmButtonText: t('embed.gameRtp.confirm'),
            cancelButtonText: t('embed.gameRtp.cancel'),
        })
        lastRefresh.value = new Date().toLocaleString()
        ElMessage.success(t('embed.gameRtp.imported'))
    } catch {
        return
    }
}

onMounted(loadGameRtp)
</script>

<style scoped lang="scss">
.game-rtp-page {
    padding: var(--ba-main-space);
    color: var(--el-text-color-primary);
}

.setting-grid {
    display: grid;
    grid-template-columns: minmax(520px, 7fr) minmax(360px, 3fr);
    gap: 14px;
    margin-bottom: 14px;
}

.panel {
    overflow: hidden;
    border: 1px solid var(--el-border-color);
    border-radius: 4px;
    background: var(--el-bg-color);
}

.panel-title {
    padding: 10px 14px;
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    background: #333;
}

.panel-body {
    padding: 16px;
}

.setting-form {
    min-height: 580px;
}

.form-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;

    > label {
        width: 205px;
        flex: 0 0 205px;
        text-align: right;
        font-weight: 600;
    }

    :deep(.el-input),
    :deep(.el-select) {
        flex: 1;
    }
}

.textarea-row {
    align-items: flex-start;
}

.form-hint {
    margin: -6px 0 12px 217px;
    color: var(--el-color-danger);
    font-size: 12px;
    font-weight: 600;
}

.color-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px 20px;
    margin: 18px 0;
}

.color-item {
    display: flex;
    align-items: center;
    gap: 8px;

    label {
        min-width: 145px;
        text-align: right;
        font-weight: 600;
    }

    span {
        color: var(--el-text-color-secondary);
        font-size: 12px;
        font-family: monospace;
    }
}

.save-button {
    margin-left: 217px;
    min-width: 110px;
}

.embed-panel {
    :deep(textarea) {
        padding: 14px;
        color: var(--el-text-color-primary);
        background: var(--el-fill-color-light);
        font-family: monospace;
        font-size: 12px;
        line-height: 1.55;
    }
}

.copy-button {
    width: 100%;
    margin-top: 12px;
}

.filter-bar {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 14px 20px;
    padding: 16px;
}

.filter-field {
    display: flex;
    align-items: center;
    gap: 10px;

    label {
        font-weight: 600;
    }

    :deep(.el-input),
    :deep(.el-select) {
        width: 220px;
    }
}

.table-wrap {
    overflow-x: auto;
    border-top: 1px solid var(--el-border-color);
}

.image-link {
    display: block;
    overflow: hidden;
    color: var(--el-color-primary);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.table-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 16px;
}

.bottom-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
    margin-top: 14px;

    .el-button {
        height: 38px;
        margin: 0;
        font-weight: 700;
    }
}

@media (max-width: 1100px) {
    .setting-grid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 720px) {
    .game-rtp-page {
        padding: 12px;
    }

    .form-row {
        align-items: stretch;
        flex-direction: column;
        gap: 6px;

        > label {
            width: auto;
            flex-basis: auto;
            text-align: left;
        }
    }

    .form-hint,
    .save-button {
        margin-left: 0;
    }

    .color-grid {
        grid-template-columns: 1fr;
    }

    .color-item label {
        min-width: 130px;
        text-align: left;
    }

    .filter-bar,
    .filter-field {
        align-items: stretch;
        flex-direction: column;
    }

    .filter-field :deep(.el-input),
    .filter-field :deep(.el-select) {
        width: 100%;
    }

    .table-footer {
        align-items: flex-start;
        flex-direction: column;
    }

    .bottom-actions {
        grid-template-columns: 1fr;
    }
}
</style>
