<script setup>
import { useQuasar, QTable, Dialog } from "quasar";
import { defu } from "defu";
import { ref, computed, onMounted, useSlots, watch, useAttrs } from "vue";
import useLight from "../../composables/useLight";
import { useLightTableDefaults } from "../../composables/useLightProps";
import model from "../../composables/model";
import { toQuery } from "@hostlink/light";
import { list, navigateTo } from "#imports";
import { useI18n } from "vue-i18n";
import { useStorage, useSessionStorage } from "@vueuse/core";
import { useRoute } from "#imports";
const $q = useQuasar();
const { t } = useI18n();
const $route = useRoute();
const errors = ref([]);
const props = defineProps({
  fullscreen: { type: Boolean, required: false, skipCheck: true, default: false },
  noRouteFullscreenExit: { type: Boolean, required: false, skipCheck: true },
  rows: { type: Array, required: false },
  rowKey: { type: [String, Function], required: false },
  virtualScroll: { type: Boolean, required: false, skipCheck: true },
  virtualScrollTarget: { type: null, required: false },
  virtualScrollSliceSize: { type: null, required: false },
  virtualScrollSliceRatioBefore: { type: null, required: false },
  virtualScrollSliceRatioAfter: { type: null, required: false },
  virtualScrollItemSize: { type: null, required: false },
  virtualScrollStickySizeStart: { type: null, required: false },
  virtualScrollStickySizeEnd: { type: null, required: false },
  tableColspan: { type: null, required: false },
  color: { type: null, required: false },
  iconFirstPage: { type: null, required: false },
  iconPrevPage: { type: null, required: false },
  iconNextPage: { type: null, required: false },
  iconLastPage: { type: null, required: false },
  grid: { type: Boolean, required: false, skipCheck: true },
  gridHeader: { type: Boolean, required: false, skipCheck: true },
  dense: { type: Boolean, required: false, skipCheck: true, default: void 0 },
  columns: { type: Array, required: false },
  visibleColumns: { type: null, required: false },
  loading: { type: Boolean, required: false, skipCheck: true },
  title: { type: null, required: false },
  hideHeader: { type: Boolean, required: false, skipCheck: true },
  hideBottom: { type: Boolean, required: false, skipCheck: true },
  hideSelectedBanner: { type: Boolean, required: false, skipCheck: true },
  hideNoData: { type: Boolean, required: false, skipCheck: true },
  hidePagination: { type: Boolean, required: false, skipCheck: true },
  dark: { type: [Boolean, null], required: false, skipCheck: true, default: void 0 },
  flat: { type: Boolean, required: false, skipCheck: true, default: void 0 },
  bordered: { type: Boolean, required: false, skipCheck: true, default: void 0 },
  square: { type: Boolean, required: false, skipCheck: true },
  separator: { type: null, required: false },
  wrapCells: { type: Boolean, required: false, skipCheck: true },
  binaryStateSort: { type: Boolean, required: false, skipCheck: true },
  columnSortOrder: { type: null, required: false },
  noDataLabel: { type: null, required: false, default: "No data available" },
  noResultsLabel: { type: null, required: false },
  loadingLabel: { type: null, required: false, default: "Loading..." },
  selectedRowsLabel: { type: Function, required: false, skipCheck: true },
  rowsPerPageLabel: { type: null, required: false, default: "Records per page:" },
  paginationLabel: { type: Function, required: false, skipCheck: true },
  tableStyle: { type: null, required: false },
  tableClass: { type: null, required: false },
  tableHeaderStyle: { type: null, required: false },
  tableHeaderClass: { type: null, required: false },
  tableRowStyleFn: { type: Function, required: false, skipCheck: true },
  tableRowClassFn: { type: Function, required: false, skipCheck: true },
  cardContainerStyle: { type: null, required: false },
  cardContainerClass: { type: null, required: false },
  cardStyle: { type: null, required: false },
  cardClass: { type: null, required: false },
  cardStyleFn: { type: Function, required: false, skipCheck: true },
  cardClassFn: { type: Function, required: false, skipCheck: true },
  titleClass: { type: null, required: false },
  filter: { type: null, required: false },
  filterMethod: { type: Function, required: false, skipCheck: true },
  pagination: { type: null, required: false, default: () => {
    return {
      sortBy: null,
      descending: true,
      page: 1,
      rowsPerPage: 20
    };
  } },
  rowsPerPageOptions: { type: null, required: false, default: () => [5, 10, 20, 50, 100, 0] },
  selection: { type: null, required: false, default: "none" },
  selected: { type: Array, required: false, default: () => [] },
  expanded: { type: null, required: false },
  sortMethod: { type: Function, required: false, skipCheck: true },
  onFullscreen: { type: Function, required: false },
  "onUpdate:fullscreen": { type: Function, required: false },
  onRowClick: { type: Function, required: false },
  onRowDblclick: { type: Function, required: false },
  onRowContextmenu: { type: Function, required: false },
  onRequest: { type: Function, required: false },
  onSelection: { type: Function, required: false },
  "onUpdate:pagination": { type: Function, required: false },
  "onUpdate:selected": { type: Function, required: false },
  "onUpdate:expanded": { type: Function, required: false },
  onVirtualScroll: { type: Function, required: false },
  actions: { type: Array, required: false, default: () => [] },
  sortBy: { type: String, required: false },
  modelName: { type: null, required: false },
  searchable: { type: Boolean, required: false, default: false },
  onRequestData: { type: Function, required: false },
  addComponent: { type: Object, required: false },
  addComponentProps: { type: null, required: false },
  name: { type: String, required: false },
  searchStyle: { type: String, required: false },
  canExpandRow: { type: Function, required: false, default: () => true }
});
const isServerSide = props.rows == void 0;
const simpleRouteName = computed(() => {
  if ($route.name && typeof $route.name == "string") {
    return $route.name;
  }
  return "";
});
const storageKey = computed(() => {
  if (!props.name) return "l-table-storage-default";
  return "l-table-storage-" + simpleRouteName.value + "-" + props.name;
});
const saveFilters = computed({
  get() {
    if (!props.name) return false;
    return useStorage(storageKey.value, false).value;
  },
  set(value) {
    if (!props.name) return;
    const storage = useStorage(storageKey.value, false);
    storage.value = value;
  }
});
const filters = useSessionStorage(storageKey.value, {});
if (!saveFilters.value) {
  filters.value = {};
}
defineOptions({ inheritAttrs: false });
const light = useLight();
const vAttrs = useAttrs();
const L_KEYS = ["actions", "sortBy", "modelName", "searchable", "onRequestData", "addComponent", "addComponentProps", "name", "searchStyle", "canExpandRow"];
const base = useLightTableDefaults(props, vAttrs, L_KEYS);
const pagination = ref(props.pagination);
if (props.rowsPerPageOptions[0] == 0) {
  pagination.value.rowsPerPage = 0;
}
const localColumns = computed(
  () => props.columns?.map((col) => ({
    ...col,
    align: col.align ?? "left",
    field: col.field ?? col.name,
    label: t(col.label),
    searchPlaceholder: col.searchPlaceholder ? t(col.searchPlaceholder) : void 0
  }))
);
const emits = defineEmits(["request-data", "update:selected", "delete"]);
const loading = ref(false);
const actionView = computed(() => props.actions.includes("view"));
const activeEdit = computed(() => props.actions.includes("edit") || props.actions.includes("update"));
const actionDelete = computed(() => props.actions.includes("delete"));
if (props.sortBy) {
  let [sortBy, descending] = props.sortBy.split(":");
  pagination.value.sortBy = sortBy;
  pagination.value.descending = descending == "desc";
}
const hasSearch = computed(() => {
  return props.columns?.some((col) => {
    return col.searchable;
  });
});
const table = ref();
const rows = ref(props.rows ?? []);
const resolvedSearchOptions = ref({});
const localSearchOptions = ref({});
const resolveSeqs = /* @__PURE__ */ new Map();
const resolveColumnOptions = async (col) => {
  if (!col.searchOptions || !col.name) return;
  if (Array.isArray(col.searchOptions)) {
    resolvedSearchOptions.value[col.name] = col.searchOptions;
    localSearchOptions.value[col.name] = col.searchOptions;
    return;
  }
  const ctx = {
    filters: { ...filters.value },
    columnValue: filters.value[col.name],
    column: col
  };
  const seq = (resolveSeqs.get(col.name) ?? 0) + 1;
  resolveSeqs.set(col.name, seq);
  try {
    const result = await col.searchOptions(ctx);
    if (seq !== resolveSeqs.get(col.name)) return;
    resolvedSearchOptions.value[col.name] = result;
    localSearchOptions.value[col.name] = result;
  } catch {
  }
};
onMounted(async () => {
  await Promise.all(
    (props.columns ?? []).map((col) => resolveColumnOptions(col))
  );
  table.value?.requestServerInteraction();
});
watch(filters, (newFilters, oldFilters) => {
  if (!props.columns) return;
  for (const col of props.columns) {
    if (!col.searchDependsOn?.length) continue;
    if (typeof col.searchOptions !== "function") continue;
    if (!col.name) continue;
    const deps = col.searchDependsOn;
    const changed = deps.some((dep) => newFilters?.[dep] !== oldFilters?.[dep]);
    if (changed) resolveColumnOptions(col);
  }
}, { deep: true });
const primaryKey = ref(props.rowKey);
const modelName = ref(props.modelName);
const validateData = () => {
  if (primaryKey.value == null) return;
  if (props.actions.includes("edit")) {
    if (rows.value.length > 0) {
      let row = rows.value[0];
      if (!row[primaryKey.value]) {
        errors.value.push("[edit] Primary key not found in the data");
      }
    }
  }
  if (props.actions.includes("delete")) {
    if (rows.value.length > 0) {
      let row = rows.value[0];
      if (!row[primaryKey.value]) {
        errors.value.push("[delete] Primary key not found in the data");
      }
    }
  }
};
const onLocalRequest = async (p) => {
  if (!isServerSide) return;
  let localFields = {};
  let sort = "";
  if (p.pagination.sortBy) {
    sort = p.pagination.sortBy + ":" + (p.pagination.descending ? "desc" : "asc");
  }
  if (props.rowKey) {
    localFields = defu(localFields, {
      [props.rowKey]: true
    });
  }
  props.columns?.forEach((col) => {
    if (col.gql) {
      localFields = defu(localFields, col.gql);
      return;
    }
    if (col.gqlField) {
      localFields = defu(localFields, col.gqlField);
      return;
    }
    if (!col.name) return;
    if (col.name.startsWith("_")) {
      return;
    }
    localFields = defu(localFields, { [col.name]: true });
  });
  if (actionView.value) {
    localFields = defu(localFields, { canView: true });
  }
  if (actionDelete.value) {
    localFields = defu(localFields, { canDelete: true });
  }
  if (activeEdit.value) {
    localFields = defu(localFields, { canUpdate: true });
  }
  const callback = {
    sort,
    fields: localFields,
    offset: (p.pagination.page - 1) * p.pagination.rowsPerPage,
    limit: p.pagination.rowsPerPage,
    gql: {
      __args: {
        filters: getFilterValue(),
        sort
      },
      data: {
        __args: {
          limit: p.pagination.rowsPerPage,
          offset: (p.pagination.page - 1) * p.pagination.rowsPerPage
        },
        ...toQuery(localFields)
      },
      meta: {
        total: true,
        key: true,
        name: true
      }
    },
    setData(data) {
      rows.value = data.data;
      primaryKey.value = data.meta.key;
      modelName.value = data.meta.name;
      pagination.value.rowsNumber = data.meta.total;
      pagination.value.page = p.pagination.page;
      pagination.value.sortBy = p.pagination.sortBy;
      pagination.value.descending = p.pagination.descending;
      pagination.value.rowsPerPage = p.pagination.rowsPerPage;
      loading.value = false;
      if (modelName.value) {
        localStorage.setItem(`l-table-rowsPerPage-${modelName.value}`, String(pagination.value.rowsPerPage));
      }
      validateData();
    },
    loadObjects(model2, filters2 = null, fields = []) {
      return this.loadData(model2, filters2, fields);
    },
    async loadData(model2, filters2 = null, fields = []) {
      const saved = Number(localStorage.getItem(`l-table-rowsPerPage-${model2}`));
      if (saved && props.rowsPerPageOptions.includes(saved) && saved !== pagination.value.rowsPerPage) {
        pagination.value.rowsPerPage = saved;
        if (typeof p !== "undefined" && p.pagination) {
          p.pagination.rowsPerPage = saved;
        }
      }
      if (Array.isArray(fields)) {
        fields.forEach((f) => {
          localFields = defu(localFields, { [f]: true });
        });
      } else {
        localFields = defu(localFields, fields);
      }
      let localFilters = getFilterValue();
      if (filters2) {
        localFilters = defu(localFilters, filters2);
      }
      loading.value = true;
      try {
        let l = list(model2, localFields);
        l = l.filters(localFilters);
        if (sort) {
          l = l.sort(sort);
        }
        if (p.pagination.rowsPerPage != 0) {
          l = l.limit(p.pagination.rowsPerPage);
          l = l.offset((p.pagination.page - 1) * p.pagination.rowsPerPage);
        }
        this.setData(await l.fetchWithMeta());
      } catch (e) {
        $q.dialog({
          message: e,
          color: "negative"
        });
        if (e instanceof Error) {
          if (e.message == "You need to be logged to access this field") {
            navigateTo("/");
            return;
          }
        }
        this.setData({ data: [], meta: { total: 0, key: "", name: "" } });
      }
    }
  };
  loading.value = true;
  emits("request-data", callback);
};
const getFilterValue = () => {
  let f = {};
  props.columns?.forEach((col) => {
    if (col.searchable) {
      if (filters.value[col.name] !== null && filters.value[col.name] !== "" && filters.value[col.name] !== void 0) {
        const k = col.searchIndex ?? col.name;
        if (col.searchType == "boolean") {
          f[k] = filters.value[col.name];
        } else if (col.searchMethod == "range" && typeof filters.value[col.name] === "object") {
          const rangeValue = filters.value[col.name];
          const rangeFilter = {};
          if (rangeValue.min !== null && rangeValue.min !== "" && rangeValue.min !== void 0) {
            rangeFilter._gte = Number(rangeValue.min);
          }
          if (rangeValue.max !== null && rangeValue.max !== "" && rangeValue.max !== void 0) {
            rangeFilter._lte = Number(rangeValue.max);
          }
          if (Object.keys(rangeFilter).length > 0) {
            f[k] = rangeFilter;
          }
        } else if (col.searchType == "number") {
          f[k] = filters.value[col.name];
        } else if (col.searchType == "date") {
          if (filters.value[k].from) {
            f[k] = {
              _between: [filters.value[col.name].from, filters.value[col.name].to]
            };
          } else {
            f[k] = filters.value[k];
          }
        } else if (col.searchType == "select") {
          f[k] = filters.value[col.name];
        } else {
          if (col.searchMethod == "equals") {
            f[k] = filters.value[col.name];
          } else {
            f[k] = {
              contains: filters.value[col.name]
            };
          }
        }
      }
    }
  });
  return f;
};
const onFilters = () => {
  onLocalRequest({
    pagination: {
      page: 1,
      rowsPerPage: props.pagination.rowsPerPage,
      sortBy: props.pagination.sortBy,
      descending: props.pagination.descending
    }
  });
};
const slots = useSlots();
const ss = computed(() => Object.keys(slots));
const onDelete = async (id) => {
  if (modelName.value == null) return;
  try {
    await model(modelName.value).delete(id);
  } catch (e) {
    $q.notify({
      message: e.message,
      color: "negative"
    });
    return;
  }
  if (table.value) {
    table.value.requestServerInteraction();
  }
  emits("delete", id);
};
const attrs = computed(() => {
  return {
    ...base.value,
    ...props.name ? { visibleColumns: visibleColumns.value } : {},
    loadingLabel: t(props.loadingLabel),
    noDataLabel: t(props.noDataLabel),
    rowsPerPageOptions: props.rowsPerPageOptions,
    rowsPerPageLabel: t(props.rowsPerPageLabel),
    selection: props.selection,
    rowKey: props.rowKey
  };
});
const filter = ref("");
const isDark = computed(() => {
  return light.theme == "dark";
});
const hasRowExpand = computed(() => {
  return ss.value.indexOf("row-expand") >= 0;
});
const hasActions = computed(() => {
  return props.actions.length > 0 || ss.value.indexOf("actions") >= 0;
});
const localSelected = computed({
  get() {
    return props.selected;
  },
  set(val) {
    emits("update:selected", val);
  }
});
watch(() => props.rows, (val) => {
  rows.value = val;
});
function requestServerInteraction() {
  table.value && table.value.requestServerInteraction();
}
defineExpose({
  requestServerInteraction
});
const onAdd = () => {
  if (!props.addComponent) return;
  $q.dialog({
    component: props.addComponent,
    componentProps: props.addComponentProps
  }).onOk(() => {
    requestServerInteraction();
  });
};
const searchSelectFilter = (val, update, name) => {
  const allOptions = resolvedSearchOptions.value[name] ?? [];
  if (val === "") {
    update(() => {
      localSearchOptions.value[name] = allOptions;
    });
  } else {
    update(() => {
      const needle = val.toLocaleLowerCase();
      localSearchOptions.value[name] = Array.isArray(allOptions) ? allOptions.filter((v) => v.label.toLocaleLowerCase().indexOf(needle) > -1) : allOptions;
    });
  }
};
const localFilterMethod = (rows2, terms) => {
  if (!terms || Object.keys(terms).length === 0) return rows2;
  return rows2.filter((row) => {
    for (const [key, value] of Object.entries(terms)) {
      if (value === null || value === void 0 || value === "") continue;
      const cell = row[key];
      if (typeof value === "object" && value !== null && "contains" in value) {
        if (!String(cell ?? "").toLowerCase().includes(String(value.contains).toLowerCase())) return false;
      } else if (typeof value === "object" && value !== null && "_between" in value) {
        if (cell < value._between[0] || cell > value._between[1]) return false;
      } else if (typeof value === "object" && value !== null && ("_gte" in value || "_lte" in value)) {
        if ("_gte" in value && value._gte !== null && value._gte !== void 0 && cell < value._gte) return false;
        if ("_lte" in value && value._lte !== null && value._lte !== void 0 && cell > value._lte) return false;
      } else {
        if (cell !== value) return false;
      }
    }
    return true;
  });
};
const clientFilterTerms = computed(() => isServerSide ? null : getFilterValue());
const showTopRight = computed(() => {
  return props.name && hasFilters.value || props.searchable || props.name && (props.columns && props.columns.length > 0);
});
const hidableColumns = computed(() => {
  return props.columns?.filter((c) => !c.required) || [];
});
const hideColumnsStorageKey = computed(() => {
  return "l-table-hide-columns-" + simpleRouteName.value + "-" + props.name;
});
const visibleColumns = computed({
  get() {
    if (props.name) {
      const hiddenColumns = useStorage(hideColumnsStorageKey.value, []).value;
      const allHidable = hidableColumns.value.map((c) => c.name);
      return allHidable.filter((name) => !hiddenColumns.includes(name));
    } else {
      return null;
    }
  },
  set(value) {
    if (props.name && value) {
      const allHidable = hidableColumns.value.map((c) => c.name);
      const hiddenColumns = allHidable.filter((name) => !value.includes(name));
      const storage = useStorage(hideColumnsStorageKey.value, []);
      storage.value = hiddenColumns;
    }
  }
});
watch(filters, () => {
  onFilters();
}, { deep: true });
const tempFilters = ref({});
tempFilters.value = { ...filters.value };
const hasFilters = computed(() => {
  return Object.keys(filters.value).length > 0;
});
</script>

<template>
    <q-table v-bind="attrs" :loading="loading" :rows="rows" ref="table" @request="onLocalRequest" :columns="localColumns"
        v-model:pagination="pagination"
        :filter="isServerSide ? filter : clientFilterTerms"
        :filter-method="isServerSide ? void 0 : localFilterMethod"
        v-model:selected="localSelected">
        <template #header="props">
            <q-tr :props="props">
                <q-th v-if="selection != 'none'" auto-width>
                    <slot name="header-selection" :props="props">
                        <q-checkbox v-model="props.selected" :dense="table?.dense" />
                    </slot>
                </q-th>
                <q-th v-if="hasRowExpand" auto-width></q-th>
                <q-th v-if="hasActions" auto-width></q-th>
                <q-th v-for="col in props.cols" :key="col.name" :props="props">{{ col.label }}</q-th>
            </q-tr>
        </template>

        <template #top v-if="errors.length > 0">
            <div class="q-mb-sm">
                <div class="q-gutter-sm">
                    <l-alert type="negative" v-for="e in errors">{{ e }}</l-alert>
                </div>
            </div>
        </template>

        <template #top-selection="props" v-if="ss.indexOf('top-selection') >= 0">
            <slot name="top-selection" v-bind="props"></slot>
        </template>

        <template #top-left v-if="addComponent">
            <q-btn icon="sym_o_add" :label="$t('Add')" color="primary" @click="onAdd" outline />
        </template>

        <template #body="props">
            <q-tr :props="props">
                <q-td v-if="selection != 'none'" auto-width>
                    <q-checkbox v-model="props.selected" :dense="table?.dense" />
                </q-td>
                <q-td v-if="hasRowExpand" auto-width>
                    <q-btn flat round size="sm" :icon="props.expand ? 'sym_o_expand_more' : 'sym_o_expand_less'"
                        @click="props.expand = !props.expand" v-if="canExpandRow(props.row)"></q-btn>
                </q-td>

                <q-td v-if="hasActions" auto-width>
                    <div :class="{ 'text-grey-8': !isDark }" v-if="primaryKey">

                        <l-action-btn v-if="actionView && props.row.canView"
                            :to="`/${modelName}/${props.row[primaryKey]}/view`"
                            :icon="light.styles.table.actionIcons.view" />

                        <l-action-btn v-if="activeEdit && props.row.canUpdate"
                            :to="`/${modelName}/${props.row[primaryKey]}/edit`"
                            :icon="light.styles.table.actionIcons.edit" />

                        <l-delete-btn v-if="actionDelete && props.row.canDelete"
                            :icon="light.styles.table.actionIcons.delete"
                            @submit="onDelete(props.row[primaryKey])" size="sm" />

                        <slot name="actions" v-bind="props"></slot>
                    </div>

                </q-td>

                <template v-for="col in props.cols">
                    <template v-if="ss.indexOf('body-cell-' + col.name) >= 0">
                        <slot :name="'body-cell-' + col.name" v-bind="{ ...props, col, value: props.row[col.field] }"></slot>
                    </template>
                    <template v-else>
                        <q-td :key="col.name" :props="props" :auto-width="col.autoWidth ?? false" :style="col.style"><template
                                v-if="col.to" class="bg-primary">
                                <l-link :to="col.to(props.row)" v-if="col.to(props.row)">{{ col.value }}</l-link>
                            </template>
                            <template v-else-if="col.component">
                                <component :is="col.component" v-model="props.row"
                                    v-bind="col.componentProps ? col.componentProps(props.row) : null" />
                            </template>
                            <template v-else>
                                {{ col.value }}
                            </template>
                        </q-td>
                    </template>
                </template>
            </q-tr>
            <q-tr v-show="props.expand" :props="props">
                <q-td colspan="100%">
                    <slot name="row-expand" v-bind="props"></slot>
                </q-td>
            </q-tr>
        </template>

        <template #top-right="props" v-if="showTopRight">
            <slot name="top-right" v-bind="props"></slot>

            <q-btn v-if="$props.name" :disable="!hasFilters" dense flat icon="sym_o_filter_alt_off" color="primary"
                @click="filters = {};
  tempFilters = {}">
                <q-tooltip>{{ $t("Clear Filters") }}</q-tooltip>
            </q-btn>

            <q-input outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')" v-if="searchable">
                <template v-slot:append>
                    <q-icon name="search" />
                </template>
            </q-input>

            <q-btn icon="sym_o_settings" flat dense color="primary" v-if="$props.name">
                <q-popup-proxy>
                    <q-card>
                        <q-card-section>
                            <q-checkbox label="Save filters" v-model="saveFilters" />
                        </q-card-section>
                    </q-card>
                </q-popup-proxy>
            </q-btn>

            <q-btn icon="sym_o_view_column" flat dense color="primary" v-if="$props.name">
                <q-popup-proxy>
                    <q-card>
                        <q-card-section>
                            <q-option-group type="checkbox" v-model="visibleColumns" :options="hidableColumns"
                                option-value="name">
                            </q-option-group>
                        </q-card-section>
                    </q-card>
                </q-popup-proxy>
            </q-btn>

            <q-btn icon="sym_o_refresh" flat dense color="primary" @click="requestServerInteraction()">
                <q-tooltip>{{ $t("Refresh") }}</q-tooltip>
            </q-btn>
        </template>

        <!-- template v-for="col in toColumns" v-slot:[col.slot_name]="props">
            <q-td :props="props">
                <l-link :to="col.to(props.row)">
                    {{ col.field(props.row) }}
                </l-link>
            </q-td>
        </template -->

        <template #top-row="props" v-if="hasSearch">
            <q-tr>
                <q-td v-if="selection != 'none'" auto-width />
                <q-td v-if="hasRowExpand" auto-width />
                <q-td v-if="hasActions" auto-width />
                <q-td v-for="col in props.cols" :key="col.name" :style="col.style">

                    <template v-if="col.searchable">

                        <template v-if="col.searchMethod == 'range' && (!col.searchType || col.searchType == 'number')">
                            <l-search-number
                                v-model="filters[col.name]"
                                @change="onFilters"
                            />
                        </template>

                        <template v-else-if="col.searchType == 'number'">
                            <q-input style="min-width: 80px;" dense clearable filled square
                                v-model.number="tempFilters[col.name]"
                                @keydown.enter.prevent="filters[col.name] = tempFilters[col.name]"
                                @clear="delete filters[col.name]" mask="##########" :enterkeyhint="$t('search')"
                                :style="col.searchStyle"></q-input>
                        </template>

                        <template v-if="col.searchType == 'select'">
                            <q-select dense clearable filled square v-model="filters[col.name]" options-dense
                                :options="localSearchOptions[col.name]" emit-value map-options
                                :multiple="col.searchMultiple" use-input input-debounce="0" @filter="(val, update) => {
  searchSelectFilter(val, update, col.name);
}" :style="col.searchStyle" :placeholder="col.searchPlaceholder" />
                        </template>

                        <template v-if="col.searchType == 'date'">
                            <l-date-picker dense clearable filled square :outlined="false" hide-bottom-space
                                v-model="filters[col.name]" range :style="col.searchStyle" :placeholder="col.searchPlaceholder" />
                        </template>

                        <template v-if="col.searchType == 'boolean'">
                            <q-select dense clearable filled square v-model="filters[col.name]" :options="[
  { label: $t('Yes'), value: true },
  { label: $t('No'), value: false }
]" :style="col.searchStyle" emit-value map-options options-dense />

                        </template>

                        <template v-if="(!col.searchType || col.searchType == 'text') && col.searchMethod != 'range'">
                            <q-input style="min-width: 80px;" dense clearable filled square
                                v-model="tempFilters[col.name]" @keydown.enter.prevent="filters[col.name] = tempFilters[col.name]" @clear="delete filters[col.name]" :enterkeyhint="$t('search')" :placeholder="col.searchPlaceholder" :style="col.searchStyle"></q-input>

                        </template>

                    </template>
                </q-td>
            </q-tr>
        </template>

        <template v-for="slot of Object.keys($slots)" v-slot:[slot]="props">
            <slot :name="slot" v-bind="props"></slot>
        </template>

    </q-table>
</template>
