# 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"
    v-bind="$attrs"
    @row-click="onRowClick"
  >
    <template #top>
      <div
        class="flex full-width justify-between items-center q-mb-md q-pl-sm"
        style="gap: 1rem"
      >
        <q-input
          v-if="showSearchField"
          v-model="pagination.filter"
          outlined
          dense
          debounce="500"
          :placeholder="$t('common.actions.search')"
          clearable
          autofocus
        >
          <template #append>
            <q-icon name="mdi-magnify" />
          </template>
        </q-input>

        <q-select
          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 />

        <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 v-if="deleteFunction">
        <q-item-section>
          <q-btn
            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">
        <div class="flex items-center">
          {{ $t("common.ui.table.rows_per_page") }}
          <q-select
            v-model="pagination.rowsPerPage"
            class="q-mx-sm"
            dense
            borderless
            :options="rowsPerPageOptions"
          >
            <template #option="selectData">
              <q-item v-bind="selectData.itemProps">
                <q-item-section>
                  <q-item-label>{{
                    selectData.opt == 0
                      ? $t("common.ui.misc.all")
                      : selectData.opt
                  }}</q-item-label>
                </q-item-section>
              </q-item>
            </template>
          </q-select>
        </div>
        <div class="flex items-center">
          {{ pagination.from + "-" + pagination.to }}
          {{ $t("common.ui.table.of") }}
          {{ pagination.rowsNumber }}
        </div>
        <div class="flex items-center">
          <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>
      </div>
    </template>

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

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

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

const {
  columns,
  apiCall,
  outlineAdd,
  openItem,
  openItemRoute,
  addItem,
  addItemRoute,
  rowsPerPage,
  showSearchField,
  hideNoDataLabel,
  deleteFunction,
} = 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,
  },
});

const { t } = useI18n();
const router = useRouter();
const rows = ref([]);
const loading = ref(true);
const fullscreen = ref(false);
const rowsPerPageOptions = [10, 15, 25, 50];

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,
  async (newVal, oldVal) => {
    if (!oldVal || loading.value) return;

    if (
      newVal.rowsPerPage !== oldVal.rowsPerPage ||
      newVal.filter !== oldVal.filter ||
      newVal.page !== oldVal.page
    ) {
      await onRequest();
    }
  },
  { deep: true },
);

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

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

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