0

I have a parent component 'DaftarKajian' and child component 'DaftarKajianMobile' and another child component named 'EditJadwalDialog'. What I have to tried is props the fetched data from 'DaftarKajianMobile' to 'EditKajianDialog'. My question is, how to display the props data in v-text-field on 'EditKajianDialog', then when I edit and submit the data, the variable of the data is changed too. For example formData.materi has value = "Vue-JS" then i want to edit it to "VueJS and Vuetify". This is the 'DaftarKajian'

<template>
  <div v-if="mdAndDown">
    <DaftarKajianMobile :dataKajianMobile="jadwalKajian" />
  </div>
  <div v-else>
    <v-row class="mx-1">
      <span class="tw-font-medium tw-text-xl">{{ route.meta.title }}</span>
    </v-row>
    <v-row class="mb-1 d-flex justify-space-between">
      <v-col cols="5">
        <v-text-field v-model="search" label="Search" prepend-inner-icon="mdi-magnify" variant="outlined" class="ml-2" hide-details></v-text-field>
      </v-col>
      <v-col class="d-flex justify-end align-center">
        <v-btn @click="dialogAdd = !dialogAdd"><v-icon>mdi-plus</v-icon>Tambah</v-btn>
      </v-col>
    </v-row>
    <!-- V-DATA-TABLE -->
    <v-data-table :headers="(headers as any)" :items="jadwalKajian" :search="search" :loading="loading">
      <template v-slot:item="{ item }">
        <tr>
          <td class="text-capitalize">{{ item.materi }}</td>
          <td>{{ new Date(item.tanggal).formatDateID }}</td>
          <td class="text-capitalize">{{ item.namaUstadz }}</td>
          <v-tooltip text="Klik untuk copy" location="bottom">
            <template v-slot:activator="{ props }">
              <td v-bind="props" @click="copyText(item.tanggal, item.token, item.mulai, item.berakhir)" style="cursor: pointer">{{ item.token }}</td>
            </template>
          </v-tooltip>
          <td>{{ item.mulai }}</td>
          <td>{{ item.berakhir }}</td>
          <td>
            <v-tooltip text="Edit" location="bottom">
              <template v-slot:activator="{ props }">
                <v-icon v-bind="props" @click="openDialogEdit(item.id!)">mdi-pencil</v-icon>
              </template>
            </v-tooltip>
            <v-tooltip text="Delete" location="bottom">
              <template v-slot:activator="{ props }">
                <v-icon v-bind="props" @click="openDialogDelete(item.id!)">mdi-delete</v-icon>
              </template>
            </v-tooltip>
          </td>
        </tr>
      </template>
    </v-data-table>
    <!-- V-DATA-TABLE -->
    <EditJadwalDialog :dataKajian="getKajianById!" :isShow="dialogEdit" @newData="handleNewEditData" @closeDialog="dialogEdit = false" />
    <AddJadwalDialog :isShow="dialogAdd" @newData="handleNewAddData" @closeDialog="dialogAdd = false" />
    <v-dialog v-model="dialogDelete" max-width="500px">
      <v-card>
        <v-card-title class="text-h5 text-center" style="white-space: normal">Apakah Anda yakin ingin menghapus Kajian/Event ini?</v-card-title>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="blue-darken-1" variant="text" @click="dialogDelete = false">Cancel</v-btn>
          <v-btn color="red-darken-1" variant="text" @click="deleteConfirm()">OK</v-btn>
          <v-spacer></v-spacer>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { useAlertsStore } from "@/store";
import { useRoute } from "vue-router";
import { useDisplay } from "vuetify";
import { TGetKajianSchedule } from "@/types";
import { kajian } from "@/repository";
import AddJadwalDialog from "@/components/Aik/AddJadwalDialog.vue";
import EditJadwalDialog from "@/components/Aik/EditJadwalDialog.vue";
import DaftarKajianMobile from "@/components/Aik/DaftarKajianMobile.vue";
import "@/utils/date.extension";

const search = ref("");
const route = useRoute();
const alerts = useAlertsStore();
const loading = ref(false);
const dialogAdd = ref(false);
const dialogEdit = ref(false);
const dialogDelete = ref(false);
const { mdAndDown } = useDisplay();
const selectedId = ref<number>();
const jadwalKajian = ref<TGetKajianSchedule[]>([]);

const headers = ref([
  {
    align: "start",
    key: "materi",
    sortable: false,
    title: "Judul Kajian",
  },
  { align: "center", key: "tanggal", title: "Tanggal" },
  { align: "center", key: "namaUstadz", title: "Pemateri" },
  { align: "center", key: "token", title: "Token" },
  { align: "center", key: "mulai", title: "Mulai" },
  { align: "center", key: "berakhir", title: "Berakhir" },
  { align: "center", key: "actions", title: "Actions" },
]);

const fetchJadwalKajian = async () => {
  loading.value = true;
  try {
    const result = await kajian.getJadwalKajian();
    jadwalKajian.value = result;
  } catch (error) {
    if (error instanceof Error) alerts.showSnackbar(error.message, "error");
  } finally {
    loading.value = false;
  }
};

onMounted(() => {
  fetchJadwalKajian();
});

const handleNewAddData = (newData: any) => {
  try {
    console.log(newData);
    jadwalKajian.value.push(newData);
  } catch (error) {
    if (error instanceof Error) alerts.showSnackbar(error.message, "error");
  }
};

const handleNewEditData = (newData: any) => {
  replaceNewData(newData.id, newData.formData);
};

const openDialogEdit = (id: number) => {
  selectedId.value = id;
  dialogEdit.value = !dialogEdit.value;
};

const openDialogDelete = (id: number) => {
  selectedId.value = id;
  dialogDelete.value = !dialogDelete.value;
};

const getKajianById = computed(() => {
  return jadwalKajian.value.find((n) => n.id === selectedId.value);
});

const deleteConfirm = async () => {
  try {
    if (selectedId.value == null) return alerts.showSnackbar("Tidak ada Kajian/Event yang dipilih", "error");
    await kajian.deleteJadwalKajian(selectedId.value);
    const index = jadwalKajian.value.findIndex((n) => n.id === selectedId.value);
    if (index !== -1) {
      jadwalKajian.value.splice(index, 1);
      alerts.showSnackbar("Kajian/Event berhasil dihapus", "success");
    }
  } catch (error) {
    if (error instanceof Error) alerts.showSnackbar(error.message, "error");
  } finally {
    dialogDelete.value = false;
  }
};

const replaceNewData = (id: number, formData: any) => {
  const index = jadwalKajian.value.findIndex((n) => n.id === id);
  if (index != -1) {
    jadwalKajian.value[index].materi = formData.materi;
    jadwalKajian.value[index].namaUstadz = formData.namaUstadz;
    jadwalKajian.value[index].tanggal = formData.tanggal;
    jadwalKajian.value[index].mulai = formData.mulai;
    jadwalKajian.value[index].berakhir = formData.berakhir;
  }
};

const copyText = (tanggal: string, token: string, mulai: string, berakhir: string) => {
  try {
    const formattedDate = new Date(tanggal).formatDateID;
    navigator.clipboard.writeText(`
Assalamualaikum Wr Wb

Berikut adalah token untuk presensi kajian online pada hari ${formattedDate}. Token berlaku dari pukul ${mulai} WIB hingga ${berakhir} WIB.
    Link: rsml.app/simpel/presensi-kajian-token
    Token: ${token}

Terimakasih, Wassalamualaikum Wr Wb`);
    alerts.showSnackbar("Token berhasil dicopy ke clipboard", "success");
  } catch (error) {
    alerts.showSnackbar("Gagal mengcopy token", "error");
  }
};
</script>

<style lang="scss" scoped>
.text-capitalize {
  text-transform: capitalize;
}
</style>

and this is the 'DaftarKajianMobile'

<template>
  <div>
    <v-row class="mx-1 d-flex justify-space-between">
      <span class="text-h7">Daftar Kajian</span>
      <v-btn variant="plain" class="pb-3 pr-0" @click="dialogAdd = !dialogAdd">Tambah</v-btn>
    </v-row>
    <v-expansion-panels variant="accordion" class="mt-4">
      <v-expansion-panel v-for="item in dataKajianMobile" :key="item.id" class="mb-2">
        <v-expansion-panel-title class="tw-bg-slate-200">
          <p style="text-transform: capitalize">{{ item.materi }}</p>
        </v-expansion-panel-title>
        <v-expansion-panel-text class="mb-2">
          <v-table>
            <tbody class="text-title">
              <tr>
                <td class="px-2">Nama Ustadz</td>
                <td class="px-0" style="text-transform: capitalize">: {{ item.namaUstadz }}</td>
              </tr>
              <tr>
                <td class="px-2">Tanggal</td>
                <td class="px-0">: {{ new Date(item.tanggal).formatDateID }}</td>
              </tr>
              <tr>
                <td class="px-2">Token</td>
                <td class="px-0">: {{ item.token }}</td>
              </tr>
              <tr>
                <td class="px-2">Mulai</td>
                <td class="px-0">: {{ item.mulai }}</td>
              </tr>
              <tr>
                <td class="px-2">Berakhir</td>
                <td class="px-0">: {{ item.berakhir }}</td>
              </tr>
            </tbody>
          </v-table>
          <div class="text-center">
            <v-icon class="mx-2" @click="openDialogEdit(item.id!)">mdi-pencil</v-icon>
            <v-icon class="mx-2" @click="openDialogDelete(item.id!)">mdi-delete</v-icon>
            <v-icon class="mx-2" @click="copyText(item.tanggal, item.token, item.mulai, item.berakhir)">mdi-content-copy</v-icon>
          </div>
        </v-expansion-panel-text>
      </v-expansion-panel>
    </v-expansion-panels>
  </div>
  <AddJadwalDialog :isShow="dialogAdd" @closeDialog="dialogAdd = false" />
  <EditJadwalDialog :isShow="dialogEdit" :dataKajian="getKajianById!" @newData="handleNewEditData" @closeDialog="dialogEdit = false" />
  <v-dialog v-model="dialogDelete" max-width="500px">
    <v-card>
      <v-card-title class="text-h5 text-center" style="white-space: normal">Apakah Anda yakin ingin menghapus Kajian/Event ini?</v-card-title>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn color="blue-darken-1" variant="text" @click="dialogDelete = false">Cancel</v-btn>
        <v-btn color="red-darken-1" variant="text" @click="deleteConfirm()">OK</v-btn>
        <v-spacer></v-spacer>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script setup lang="ts">
import { defineProps, ref, computed, withDefaults } from "vue";
import { TGetKajianSchedule } from "@/types";
import { kajian } from "@/repository";
import { useAlertsStore } from "@/store";
import AddJadwalDialog from "./AddJadwalDialog.vue";
import EditJadwalDialog from "./EditJadwalDialog.vue";

const alerts = useAlertsStore();
const dialogAdd = ref(false);
const dialogEdit = ref(false);
const dialogDelete = ref(false);
const selectedId = ref<number>();
const props = withDefaults(
  defineProps<{
    dataKajianMobile: TGetKajianSchedule[];
  }>(),
  {
    dataKajianMobile: () => [],
  }
);

const dataKajian = props.dataKajianMobile;

const copyText = (tanggal: string, token: string, mulai: string, berakhir: string) => {
  try {
    const formattedDate = new Date(tanggal).formatDateID;
    navigator.clipboard.writeText(`Assalamualaikum Wr Wb

Berikut adalah token untuk presensi kajian online pada hari ${formattedDate}. Token berlaku dari pukul ${mulai} WIB hingga ${berakhir} WIB.

    Link: rsml.app/simpel/presensi-kajian-token
    Token: ${token}

Terimakasih, Wassalamualaikum Wr Wb`);
    alerts.showSnackbar("Token berhasil dicopy ke clipboard", "success");
  } catch (error) {
    alerts.showSnackbar("Gagal mengcopy token", "error");
  }
};

const openDialogEdit = (id: number) => {
  selectedId.value = id;
  dialogEdit.value = !dialogEdit.value;
};

const getKajianById = computed(() => {
  return dataKajian.find((n) => n.id === selectedId.value);
});

const handleNewEditData = (newData: any) => {
  replaceNewData(newData.id, newData.formData);
};

const replaceNewData = (id: number, formData: any) => {
  const index = dataKajian.findIndex((n) => n.id === id);
  if (index != -1) {
    dataKajian[index].materi = formData.materi;
    dataKajian[index].namaUstadz = formData.namaUstadz;
    dataKajian[index].tanggal = formData.tanggal;
    dataKajian[index].mulai = formData.mulai;
    dataKajian[index].berakhir = formData.berakhir;
  }
};

const openDialogDelete = (id: number) => {
  selectedId.value = id;
  dialogDelete.value = !dialogDelete.value;
};

const deleteConfirm = async () => {
  try {
    if (selectedId.value == null) return alerts.showSnackbar("Tidak ada Kajian/Event yang dipilih", "error");
    await kajian.deleteJadwalKajian(selectedId.value);
    const index = dataKajian.findIndex((n) => n.id === selectedId.value);
    if (index !== -1) {
      dataKajian.splice(index, 1);
      alerts.showSnackbar("Kajian/Event berhasil dihapus", "success");
    }
  } catch (error) {
    if (error instanceof Error) alerts.showSnackbar(error.message, "error");
  } finally {
    dialogDelete.value = false;
  }
};
</script>

<style lang="scss" scoped>
:deep(.v-expansion-panel-text__wrapper) {
  padding: 0%;
}
.text-title {
  font-size: calc(6px + 2vw);
}
</style>

and this is the 'EditKajianDialog'

<template>
  <v-dialog :model-value="props.isShow" max-width="700">
    <v-card prepend-icon="mdi-calendar" title="Edit Jadwal Kajian">
      <v-card-text>
        <v-row dense>
          <v-col cols="12" md="6" sm="6">
            <v-text-field
              label="Judul/Materi Kajian"
              v-model="formData.materi"
              :input-classes="{ capitalize: true }"
              style="text-transform: capitalize"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6" sm="6">
            <v-text-field label="Nama Ustadz" v-model="formData.namaUstadz" :input-classes="{ capitalize: true }"></v-text-field>
          </v-col>

          <v-col cols="12" md="4" sm="6">
            <v-menu v-model="showPicker" :close-on-content-click="false">
              <template v-slot:activator="{ props: activatorProps }">
                <v-text-field v-bind="activatorProps" label="Tanggal Kajian" readonly :model-value="formattedDate"></v-text-field>
              </template>
              <v-date-picker v-model="selectedDate" @update:model-value="showPicker = false"></v-date-picker>
            </v-menu>
          </v-col>

          <v-col cols="12" md="4" sm="6">
            <v-text-field
              type="time"
              step="1"
              min="00:00:00"
              max="23:59:59"
              label="Masa Berlaku Token (Mulai)"
              v-model="formData.mulai"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="4" sm="6">
            <v-text-field
              type="time"
              step="1"
              min="00:00:00"
              max="23:59:59"
              label="Masa Berlaku Token (Berakhir)"
              v-model="formData.berakhir"
            ></v-text-field>
          </v-col>
        </v-row>
      </v-card-text>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn color="error" text="Close" variant="plain" @click="$emit('closeDialog')"></v-btn>
        <v-btn color="success" text="Submit" variant="tonal" @click="submit"></v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script setup lang="ts">
import { reactive, ref, watch, computed } from "vue";
import { kajian } from "@/repository";
import { TGetKajianSchedule } from "@/types";
import { useAlertsStore } from "@/store";

const props = defineProps<{
  isShow: boolean;
  dataKajian: TGetKajianSchedule;
}>();
const emits = defineEmits(["newData", "closeDialog"]);
const alerts = useAlertsStore();
const showPicker = ref(false);
const selectedDate = ref(new Date());
const formData = reactive({
  tanggal: "",
  materi: "",
  namaUstadz: "",
  mulai: "",
  berakhir: "",
  token: "",
});

const submit = async () => {
  console.log(formData);
  try {
    if (!formData.namaUstadz || !formData.materi || !formData.mulai || !formData.berakhir)
      return alerts.showSnackbar("Mohon isi semua input dengan lengkap!", "error");
    console.log(formData);
    await kajian.putJadwalKajian(props.dataKajian!.id!, formData);
    const newData = { id: props.dataKajian!.id, formData };
    alerts.showSnackbar("Berhasil mengubah!", "success");
    emits("newData", newData);
    emits("closeDialog");
  } catch (error) {
    if (error instanceof Error) alerts.showSnackbar(error.message, "error");
  }
};

const showData = () => {
  console.log(props.dataKajian);
  if (props.dataKajian) {
    formData.tanggal = props.dataKajian.tanggal;
    formData.materi = props.dataKajian.materi;
    formData.namaUstadz = props.dataKajian.namaUstadz;
    formData.mulai = props.dataKajian.mulai;
    formData.berakhir = props.dataKajian.berakhir;
    formData.token = props.dataKajian.token;
  }
};

watch(
  () => props.isShow,
  (val) => {
    if (val) {
      showData();
    }
  }
);

const formattedDate = computed(() => {
  return new Date(formData.tanggal).formatDateID;
});

watch(selectedDate, (newValue) => {
  formData.tanggal = newValue.toISOStringOffset();
});
</script>

<style lang="scss" scoped>
.capitalize {
  text-transform: capitalize;
}
</style>

Sorry for my bad English

0