# DefaultTableServerSide.vue
<template>
  <q-table
    v-model:fullscreen="fullscreen"
    v-model:pagination="pagination"
    row-key="id"
    flat
    class="softpar-table q-pa-sm"
    :pagination-label="getPaginationLabel"
    :rows="rows"
    :rows-per-page-label="$t('common.ui.table.rows_per_page')"
    :columns="columns"
    :visible-columns="visibleColumns"
    :filter="pagination.filter"
    :grid="$q.screen.lt.sm"
    :loading="loading"
    :hide-top="semTableTop"
    v-bind="$attrs"
    @row-click="onRowClick"
  >
    <template v-if="!semTableTop" #top>
      <div
        class="flex full-width justify-between items-center"
      >
        <DefaultInput
          v-if="showSearchField"
          v-model="pagination.filter"
          outlined
          dense
          debounce="500"
          :placeholder="$t('associado.search_placeholder')"
          clearable
          autofocus
          color="violet-normal"
        >
          <template #prepend>
            <q-icon name="mdi-magnify" />
          </template>
        </DefaultInput>

        <DefaultSelect
          v-if="showColumnsSelect"
          v-model="visibleColumns"
          class="q-ml-md"
          multiple
          dense
          outlined
          options-outlined
          :display-value="$q.lang.table.columns"
          emit-value
          map-options
          :options="mapColumns"
          style="min-width: 150px"
          options-selected-class="text-bold"
        />

        <q-space v-if="showSearchField || showColumnsSelect" />

        <q-btn
          v-if="addItem"
          color="primary"
          padding="12px 16px"
          :outline="outlineAdd"
          :label="labelAdd"
          @click="onAddItem"
        />
      </div>
    </template>

    <template #body-cell-actions="{ row }">
      <q-td auto-width>
        <q-item-section class="no-wrap" style="flex-direction: row">
          <slot name="body-cell-actions" :row="row" />
          <q-btn
            v-if="deleteFunction"
            color="negative"
            flat
            dense
            icon="mdi-delete"
            style="width: 45px"
            class="q-ml-auto q-mr-sm"
            @click.prevent.stop="onDelete(row.id)"
          />
        </q-item-section>
      </q-td>
    </template>

    <template v-if="!hideNoDataLabel" #no-data>
      <div class="q-my-md row justify-center full-width">
        <q-spinner v-if="loading" color="primary" size="30px" />
        <div v-else class="q-pa-md body2">
          {{ $t("http.errors.no_records_found") }}
        </div>
      </div>
    </template>

    <template #bottom="scope">
      <div class="flex full-width justify-end items-center" style="gap: 8px">
        <span class="text-caption text-grey-7">
          {{ $t("common.ui.table.rows_per_page") }} 10 &nbsp;|&nbsp;
          {{ pagination.from }}-{{ pagination.to }}
          {{ $t("common.ui.table.of") }}
          {{ pagination.rowsNumber }}
        </span>
        <q-btn
          icon="mdi-chevron-left"
          color="grey-8"
          round
          dense
          flat
          :disable="scope.isFirstPage"
          @click="prevPage"
        />
        <q-btn
          icon="mdi-chevron-right"
          color="grey-8"
          round
          dense
          flat
          :disable="scope.isLastPage"
          @click="nextPage"
        />
      </div>
    </template>

    <template v-for="slotName in usableSlots($slots)" #[slotName]="data">
      <slot :name="slotName" v-bind="data" />
    </template>
  </q-table>
</template>

<script setup>
import { ref, computed, onMounted, toRaw, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";

import DefaultInput from "./DefaultInput.vue";
import DefaultSelect from "./DefaultSelect.vue";

const emit = defineEmits([
  "onRowClick",
  "onAddItem",
  "noRows",
  "togglePrincipal",
]);

const {
  columns,
  apiCall,
  outlineAdd,
  openItem,
  openItemRoute,
  addItem,
  addItemRoute,
  rowsPerPage,
  showSearchField,
  hideNoDataLabel,
  deleteFunction,
  semTableTop,
} = defineProps({
  columns: {
    type: Array,
    required: true,
  },
  apiCall: {
    type: Function,
    required: true,
  },
  labelAdd: {
    type: String,
    default: "Adicionar",
  },
  outlineAdd: {
    type: Boolean,
    default: false,
  },
  openItem: {
    type: Boolean,
    default: false,
  },
  openItemRoute: {
    type: String,
    default: "",
  },
  addItem: {
    type: Boolean,
    default: true,
  },
  addItemRoute: {
    type: String,
    default: "",
  },
  rowsPerPage: {
    type: Number,
    default: 10,
  },
  showColumnsSelect: {
    type: Boolean,
    default: false,
  },
  showSearchField: {
    type: Boolean,
    default: true,
  },
  noApiRoute: {
    type: Boolean,
    default: false,
  },
  hideNoDataLabel: {
    type: Boolean,
    default: false,
  },
  deleteFunction: {
    type: Function,
    default: null,
  },
  semTableTop: {
    type: Boolean,
    default: false,
  },
});

const { t } = useI18n();
const router = useRouter();
const rows = ref([]);
const loading = ref(true);
const fullscreen = ref(false);

const pagination = ref({
  filter: undefined,
  page: 1,
  rowsPerPage: rowsPerPage,
  rowsNumber: 0,
  from: 0,
  to: 0,
});

const mapColumns = computed(() => {
  return columns.reduce((accm, column) => {
    if (!column.required) {
      accm.push({
        label: column.label.toUpperCase(),
        value: column.name,
      });
    }
    return accm;
  }, []);
});

const visibleColumns = ref(mapColumns.value.map((column) => column.value));

const onRowClick = (evt, row, index) => {
  const item = toRaw(row);
  if (openItem) {
    if (openItemRoute) {
      router.push({ name: openItemRoute, params: { id: item.id } });
    } else {
      emit("onRowClick", { evt, row, index });
    }
  }
};

const onAddItem = () => {
  if (addItem) {
    if (addItemRoute) {
      router.push({ name: addItemRoute });
    } else {
      emit("onAddItem");
    }
  }
};

const onDelete = async (id) => {
  if (deleteFunction) {
    loading.value = true;
    try {
      await deleteFunction(id);
      await onRequest();
    } catch (error) {
      console.error(error);
    } finally {
      loading.value = false;
    }
  }
};

const prevPage = () => {
  pagination.value.page--;
  onRequest();
};

const nextPage = () => {
  pagination.value.page++;
  onRequest();
};

const getPaginationLabel = (from, to, total) => {
  return `${from}-${to} ${t?.("common.ui.table.of") ?? "of"} ${total}`;
};

let isFetching = false;
const onRequest = async () => {
  if (isFetching) return;

  isFetching = true;
  loading.value = true;

  try {
    const response = await apiCall({
      page: pagination.value.page,
      perPage: pagination.value.rowsPerPage,
      filter: pagination.value.filter,
    });

    rows.value = response.data.result.data;
    pagination.value.rowsNumber = response.data.result.total;
    pagination.value.from = response.data.result.from;
    pagination.value.to = response.data.result.to;

    if (rows.value.length === 0) {
      emit("noRows");
    }
  } catch (error) {
    console.error("Error fetching data:", error);
  } finally {
    loading.value = false;
    isFetching = false;
  }
};

watch(
  () => apiCall,
  () => onRequest(),
);

watch(
  () => pagination.value.filter,
  (newFilter, oldFilter) => {
    if (newFilter === oldFilter || loading.value) return;
    pagination.value.page = 1;
    onRequest();
  },
);

watch(
  () => pagination.value.rowsPerPage,
  (newVal, oldVal) => {
    if (newVal === oldVal || loading.value) return;
    pagination.value.page = 1;
    onRequest();
  },
);

onMounted(async () => {
  await onRequest();
});

const usableSlots = (slots) => {
  const handled = ["top", "body-cell-actions", "no-data", "bottom", "loading"];
  return Object.keys(slots).filter((s) => !handled.includes(s));
};

defineExpose({
  refresh: onRequest,
});
</script>

<style lang="scss">
@import "src/css/table.scss";
</style>
