|
|
@@ -0,0 +1,316 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <DefaultHeaderPage />
|
|
|
+
|
|
|
+ <div class="q-pa-md">
|
|
|
+ <q-card flat class="profile-card">
|
|
|
+
|
|
|
+ <div class="profile-header row items-center q-pa-lg">
|
|
|
+ <div class="profile-avatar-wrap q-mr-md">
|
|
|
+ <q-avatar size="76px" class="profile-avatar">
|
|
|
+ <q-icon name="mdi-account" size="44px" color="white" />
|
|
|
+ </q-avatar>
|
|
|
+ <q-btn
|
|
|
+ round
|
|
|
+ unelevated
|
|
|
+ size="xs"
|
|
|
+ icon="mdi-pencil-outline"
|
|
|
+ class="profile-avatar-edit"
|
|
|
+ color="white"
|
|
|
+ text-color="violet-normal"
|
|
|
+ disable
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="column q-gutter-xs">
|
|
|
+ <span class="text-white text-h6 text-weight-bold">{{ user?.name || '—' }}</span>
|
|
|
+ <div>
|
|
|
+ <q-badge
|
|
|
+ :color="statusBadgeColor"
|
|
|
+ class="text-capitalize"
|
|
|
+ style="font-size:11px; padding: 3px 8px; border-radius: 20px;"
|
|
|
+ >
|
|
|
+ {{ statusLabel }}
|
|
|
+ </q-badge>
|
|
|
+ </div>
|
|
|
+ <span class="text-white profile-validity text-caption">
|
|
|
+ {{ $t('associado.validity') }} {{ user?.expiry_date || '—' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="q-pa-md column">
|
|
|
+ <div
|
|
|
+ v-for="field in infoFields"
|
|
|
+ :key="field.label"
|
|
|
+ class="info-row row items-center no-wrap q-my-sm q-px-md"
|
|
|
+ >
|
|
|
+ <q-icon :name="field.icon" size="22px" class="info-icon q-mr-md" />
|
|
|
+ <div class="column">
|
|
|
+ <span class="info-label">{{ field.label }}</span>
|
|
|
+ <span class="info-value">{{ field.value || '—' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="q-px-md">
|
|
|
+ <div class="text-h6">{{ $t('associado.dependents') }}</div>
|
|
|
+
|
|
|
+ <q-list v-if="dependents.length > 0" class="column q-mb-md">
|
|
|
+ <div
|
|
|
+ v-for="dep in dependents"
|
|
|
+ :key="dep.id"
|
|
|
+ class="dependent-row row items-center no-wrap q-my-md"
|
|
|
+ >
|
|
|
+ <q-avatar size="40px" class="dependent-avatar q-mr-md" text-color="white">
|
|
|
+ {{ initials(dep.name) }}
|
|
|
+ </q-avatar>
|
|
|
+
|
|
|
+ <div class="col column">
|
|
|
+ <span class="dependent-name">{{ dep.name }}</span>
|
|
|
+ <span class="dependent-kinship">
|
|
|
+ {{ $t(`associado.kinship_options.${dep.kinship}`) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-badge
|
|
|
+ :color="dependentStatusColor(dep.status)"
|
|
|
+ class="q-mr-sm text-capitalize"
|
|
|
+ style="font-size:11px; padding: 3px 8px; border-radius: 20px;"
|
|
|
+ >
|
|
|
+ {{ dependentStatusLabel(dep.status) }}
|
|
|
+ </q-badge>
|
|
|
+ </div>
|
|
|
+ </q-list>
|
|
|
+
|
|
|
+ <div v-else class="text-center q-py-md text-grey">
|
|
|
+ {{ $t('associado.no_dependents') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-end q-pb-md">
|
|
|
+ <q-btn
|
|
|
+ unelevated
|
|
|
+ icon="mdi-plus"
|
|
|
+ :label="$t('common.actions.add').toUpperCase()"
|
|
|
+ color="violet-normal"
|
|
|
+ text-color="white"
|
|
|
+ style="border-radius: 8px; padding: 8px 20px;"
|
|
|
+ @click="onAddDependent"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </q-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onMounted, defineAsyncComponent } from "vue";
|
|
|
+import { useQuasar } from "quasar";
|
|
|
+import { useI18n } from "vue-i18n";
|
|
|
+import { userStore } from "src/stores/user";
|
|
|
+import { getDependentsByUser } from "src/api/profile";
|
|
|
+
|
|
|
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
|
|
|
+
|
|
|
+const AddEditDependentDialog = defineAsyncComponent(
|
|
|
+ () => import("src/pages/associado/profile/components/AddEditDependentDialog.vue"),
|
|
|
+);
|
|
|
+
|
|
|
+const $q = useQuasar();
|
|
|
+const { t } = useI18n();
|
|
|
+const store = userStore();
|
|
|
+
|
|
|
+const user = ref(null);
|
|
|
+const dependents = ref([]);
|
|
|
+
|
|
|
+const statusBadgeColor = computed(() => {
|
|
|
+ switch (user.value?.status) {
|
|
|
+ case "active": return "positive";
|
|
|
+ case "inactive": return "warning";
|
|
|
+ case "canceled": return "negative";
|
|
|
+ default: return "grey";
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const statusLabel = computed(() => {
|
|
|
+ const map = { active: t("common.status.active"), inactive: t("common.status.inactive"), canceled: t("common.status.canceled") };
|
|
|
+ return map[user.value?.status] ?? user.value?.status ?? "—";
|
|
|
+});
|
|
|
+
|
|
|
+const infoFields = computed(() => [
|
|
|
+ { icon: "mdi-card-account-details-outline", label: t("common.terms.cpf"), value: user.value?.cpf },
|
|
|
+ { icon: "mdi-email-outline", label: "E-mail", value: user.value?.email },
|
|
|
+ { icon: "mdi-briefcase-outline", label: t("associado.position"), value: user.value?.position?.name },
|
|
|
+ { icon: "mdi-domain", label: t("associado.sector"), value: user.value?.sector?.name },
|
|
|
+ { icon: "mdi-card-text-outline", label: t("associado.registration"), value: user.value?.registration },
|
|
|
+ { icon: "mdi-calendar-outline", label: t("associado.admission_date"), value: user.value?.admission_date },
|
|
|
+]);
|
|
|
+
|
|
|
+const initials = (name) => {
|
|
|
+ if (!name) return "?";
|
|
|
+ return name.split(" ").slice(0, 2).map((w) => w[0]).join("").toUpperCase();
|
|
|
+};
|
|
|
+
|
|
|
+const dependentStatusColor = (status) => {
|
|
|
+ switch (status) {
|
|
|
+ case "approved": return "positive";
|
|
|
+ case "refused": return "negative";
|
|
|
+ case "pending": return "warning";
|
|
|
+ default: return "grey";
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const dependentStatusLabel = (status) => {
|
|
|
+ const map = {
|
|
|
+ approved: t("associado.dependent_statuses.approved"),
|
|
|
+ refused: t("associado.dependent_statuses.refused"),
|
|
|
+ pending: t("associado.dependent_statuses.pending"),
|
|
|
+ };
|
|
|
+ return map[status] ?? status ?? "—";
|
|
|
+};
|
|
|
+
|
|
|
+const loadUser = async () => {
|
|
|
+ await store.fetchUser();
|
|
|
+ user.value = store.user;
|
|
|
+};
|
|
|
+
|
|
|
+const loadDependents = async () => {
|
|
|
+ if (!user.value?.id) return;
|
|
|
+ try {
|
|
|
+ dependents.value = await getDependentsByUser(user.value.id);
|
|
|
+ } catch {
|
|
|
+ dependents.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await loadUser();
|
|
|
+ await loadDependents();
|
|
|
+});
|
|
|
+
|
|
|
+const onAddDependent = () => {
|
|
|
+ $q.dialog({
|
|
|
+ component: AddEditDependentDialog,
|
|
|
+ componentProps: {
|
|
|
+ userId: user.value.id,
|
|
|
+ title: () => t("common.actions.add") + " " + t("associado.dependent"),
|
|
|
+ },
|
|
|
+ }).onOk(() => loadDependents());
|
|
|
+};
|
|
|
+
|
|
|
+// const onEditDependent = (dep) => {
|
|
|
+// $q.dialog({
|
|
|
+// component: AddEditDependentDialog,
|
|
|
+// componentProps: {
|
|
|
+// dependent: dep,
|
|
|
+// userId: user.value.id,
|
|
|
+// title: () => t("common.actions.edit") + " " + t("associado.dependent"),
|
|
|
+// },
|
|
|
+// }).onOk(() => loadDependents());
|
|
|
+// };
|
|
|
+
|
|
|
+// const onDeleteDependent = (dep) => {
|
|
|
+// $q.dialog({
|
|
|
+// title: t("common.ui.messages.confirm_action"),
|
|
|
+// message: t("common.ui.messages.are_you_sure_delete"),
|
|
|
+// cancel: true,
|
|
|
+// persistent: true,
|
|
|
+// }).onOk(async () => {
|
|
|
+// try {
|
|
|
+// await deleteDependent(dep.id);
|
|
|
+// await loadDependents();
|
|
|
+// $q.notify({ type: "positive", message: t("http.success") });
|
|
|
+// } catch {
|
|
|
+// $q.notify({ type: "negative", message: t("http.errors.failed") });
|
|
|
+// }
|
|
|
+// });
|
|
|
+// };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+@use "src/css/quasar.variables.scss" as *;
|
|
|
+
|
|
|
+.profile-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.profile-header {
|
|
|
+ background: linear-gradient(135deg, $violet-normal 0%, $violet-dark 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.profile-avatar-wrap {
|
|
|
+ position: relative;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.profile-avatar {
|
|
|
+ background: rgba(255, 255, 255, 0.18) !important;
|
|
|
+ border: 2px solid rgba(255, 255, 255, 0.45);
|
|
|
+}
|
|
|
+
|
|
|
+.profile-avatar-edit {
|
|
|
+ position: absolute;
|
|
|
+ bottom: -2px;
|
|
|
+ right: -2px;
|
|
|
+ width: 22px;
|
|
|
+ height: 22px;
|
|
|
+ min-height: 22px;
|
|
|
+}
|
|
|
+
|
|
|
+.profile-validity {
|
|
|
+ opacity: 0.85;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row {
|
|
|
+ background: $violet-light;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-icon {
|
|
|
+ color: $violet-normal;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.info-label {
|
|
|
+ font-size: 11px;
|
|
|
+ color: $color-text-2;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value {
|
|
|
+ font-size: 14px;
|
|
|
+ color: $color-text;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.dependent-row {
|
|
|
+ background: $violet-light;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 10px 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.dependent-avatar {
|
|
|
+ background: $violet-normal !important;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 700;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.dependent-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: $color-text;
|
|
|
+ line-height: 1.3;
|
|
|
+}
|
|
|
+
|
|
|
+.dependent-kinship {
|
|
|
+ font-size: 12px;
|
|
|
+ color: $color-text-2;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+</style>
|