Compare commits
3 Commits
80186bf493
...
af368eeee0
| Author | SHA1 | Date | |
|---|---|---|---|
| af368eeee0 | |||
| e104cd8fcc | |||
| 50801e5c8a |
22
QWEN.md
22
QWEN.md
@@ -229,4 +229,24 @@ Common issues and solutions:
|
||||
3. Test database changes with `bunx prisma db push`
|
||||
4. Use the integrated Swagger docs at `/api/docs` for API testing
|
||||
5. Check environment variables are properly configured
|
||||
6. Verify responsive design on different screen sizes
|
||||
6. Verify responsive design on different screen sizes
|
||||
|
||||
## Qwen Added Memories
|
||||
- **GitHub Workflow Execution**: Project ini memiliki 3 workflow GitHub Action:
|
||||
1. `publish.yml` - Build & push Docker image ke GHCR (manual trigger, butuh input: stack_env + tag)
|
||||
2. `re-pull.yml` - Re-pull Docker image di Portainer (manual trigger, butuh input: stack_name + stack_env)
|
||||
3. `docker-publish.yml` - Auto build & push saat ada tag versi v*
|
||||
|
||||
Workflow bisa dijalankan via GitHub CLI: `gh workflow run <nama.yml> -f param=value --ref branch`
|
||||
|
||||
Setelah commit ke branch deployment (dev/stg/prod), otomatis trigger workflow publish + re-pull untuk deploy ke server.
|
||||
- **Deployment Workflow Sistematis**:
|
||||
1. **Version Bump** - Update `version` di `package.json` sebelum deploy (ikuti semver: major.minor.patch)
|
||||
2. **Commit** - Commit perubahan + version bump dengan pesan yang jelas
|
||||
3. **Push ke Branch** - Push ke branch target (biasanya `stg` untuk staging atau `prod` untuk production)
|
||||
4. **Trigger Publish** - Jalankan `gh workflow run publish.yml --ref <branch> -f stack_env=<env> -f tag=<version>`
|
||||
5. **Trigger Re-Pull** - Jalankan `gh workflow run re-pull.yml -f stack_name=desa-darmasaba -f stack_env=<env>`
|
||||
6. **Verifikasi** - Cek workflow berhasil dan aplikasi berjalan
|
||||
|
||||
Branch deployment: `stg` (staging) atau `prod` (production)
|
||||
Version format di package.json: `"version": "major.minor.patch"`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "desa-darmasaba",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -9,7 +9,6 @@ const templateMigrasiPenduduk = z.object({
|
||||
tanggal: z.string().min(1, "Tanggal harus diisi"),
|
||||
asalTujuan: z.string().min(1, "Asal/Tujuan harus diisi"),
|
||||
alasan: z.string().optional(),
|
||||
jenisKelamin: z.string().optional(),
|
||||
});
|
||||
|
||||
const migrasiPenduduk = proxy({
|
||||
@@ -20,7 +19,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: "",
|
||||
asalTujuan: "",
|
||||
alasan: "",
|
||||
jenisKelamin: "",
|
||||
},
|
||||
loading: false,
|
||||
async create() {
|
||||
@@ -38,7 +36,7 @@ const migrasiPenduduk = proxy({
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Sukses menambahkan data migrasi penduduk");
|
||||
migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "", jenisKelamin: "" };
|
||||
migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "" };
|
||||
migrasiPenduduk.findMany.load();
|
||||
return id;
|
||||
}
|
||||
@@ -116,7 +114,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: "",
|
||||
asalTujuan: "",
|
||||
alasan: "",
|
||||
jenisKelamin: "",
|
||||
},
|
||||
loading: false,
|
||||
async submit() {
|
||||
@@ -132,7 +129,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: this.form.tanggal,
|
||||
asalTujuan: this.form.asalTujuan,
|
||||
alasan: this.form.alasan,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
};
|
||||
|
||||
const cek = templateMigrasiPenduduk.safeParse(formData);
|
||||
|
||||
@@ -28,7 +28,6 @@ interface MigrasiPendudukForm {
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
alasan: string;
|
||||
jenisKelamin: string;
|
||||
}
|
||||
|
||||
export default function EditMigrasiPenduduk() {
|
||||
@@ -42,7 +41,6 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<MigrasiPendudukForm>({
|
||||
jenis: '',
|
||||
@@ -50,7 +48,6 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
});
|
||||
|
||||
const jenisOptions = [
|
||||
@@ -58,11 +55,6 @@ export default function EditMigrasiPenduduk() {
|
||||
{ value: 'KELUAR', label: 'Keluar' },
|
||||
];
|
||||
|
||||
const jenisKelaminOptions = [
|
||||
{ value: 'L', label: 'Laki-laki' },
|
||||
{ value: 'P', label: 'Perempuan' },
|
||||
];
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.jenis?.trim() !== '' &&
|
||||
@@ -81,23 +73,23 @@ export default function EditMigrasiPenduduk() {
|
||||
stateMigrasiPenduduk.update.id = id;
|
||||
await stateMigrasiPenduduk.findUnique.load(id);
|
||||
|
||||
const data = stateMigrasiPenduduk.findUnique.data as MigrasiPendudukForm | null;
|
||||
const data = stateMigrasiPenduduk.findUnique.data as any;
|
||||
if (data) {
|
||||
const asalTujuan = data.jenis === 'MASUK' ? (data.asal || '') : (data.tujuan || '');
|
||||
|
||||
setFormData({
|
||||
jenis: data.jenis ?? '',
|
||||
nama: data.nama ?? '',
|
||||
tanggal: data.tanggal ?? '',
|
||||
asalTujuan: data.asalTujuan ?? '',
|
||||
tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
|
||||
asalTujuan: asalTujuan,
|
||||
alasan: data.alasan ?? '',
|
||||
jenisKelamin: data.jenisKelamin ?? '',
|
||||
});
|
||||
setOriginalData({
|
||||
jenis: data.jenis ?? '',
|
||||
nama: data.nama ?? '',
|
||||
tanggal: data.tanggal ?? '',
|
||||
asalTujuan: data.asalTujuan ?? '',
|
||||
tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
|
||||
asalTujuan: asalTujuan,
|
||||
alasan: data.alasan ?? '',
|
||||
jenisKelamin: data.jenisKelamin ?? '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -114,7 +106,7 @@ export default function EditMigrasiPenduduk() {
|
||||
const handleChangeText = useCallback(
|
||||
(field: keyof MigrasiPendudukForm) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value as never }));
|
||||
setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -122,7 +114,7 @@ export default function EditMigrasiPenduduk() {
|
||||
const handleChangeSelect = useCallback(
|
||||
(field: keyof MigrasiPendudukForm) =>
|
||||
(value: string | null) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: (value || '') as never }));
|
||||
setFormData((prev) => ({ ...prev, [field]: value || '' }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -134,7 +126,6 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: originalData.tanggal,
|
||||
asalTujuan: originalData.asalTujuan,
|
||||
alasan: originalData.alasan,
|
||||
jenisKelamin: originalData.jenisKelamin,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
@@ -211,6 +202,7 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: val || '',
|
||||
}));
|
||||
}}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -231,14 +223,6 @@ export default function EditMigrasiPenduduk() {
|
||||
minRows={2}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Pilih jenis kelamin"
|
||||
data={jenisKelaminOptions}
|
||||
value={formData.jenisKelamin}
|
||||
onChange={handleChangeSelect('jenisKelamin')}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -31,11 +31,6 @@ function CreateMigrasiPenduduk() {
|
||||
{ value: 'KELUAR', label: 'Keluar' },
|
||||
];
|
||||
|
||||
const jenisKelaminOptions = [
|
||||
{ value: 'L', label: 'Laki-laki' },
|
||||
{ value: 'P', label: 'Perempuan' },
|
||||
];
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateMigrasiPenduduk.create.form.jenis?.trim() !== '' &&
|
||||
@@ -52,12 +47,12 @@ function CreateMigrasiPenduduk() {
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stateMigrasiPenduduk.create.create();
|
||||
if (id) {
|
||||
resetForm();
|
||||
@@ -126,6 +121,7 @@ function CreateMigrasiPenduduk() {
|
||||
onChange={(val: string | null) => {
|
||||
stateMigrasiPenduduk.create.form.tanggal = val || '';
|
||||
}}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -150,16 +146,6 @@ function CreateMigrasiPenduduk() {
|
||||
minRows={2}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Pilih jenis kelamin"
|
||||
data={jenisKelaminOptions}
|
||||
value={stateMigrasiPenduduk.create.form.jenisKelamin}
|
||||
onChange={(val) => {
|
||||
stateMigrasiPenduduk.create.form.jenisKelamin = val || '';
|
||||
}}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -57,9 +57,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
jenis: string;
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
asal: string | null;
|
||||
tujuan: string | null;
|
||||
alasan: string | null;
|
||||
jenisKelamin: string | null;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
@@ -142,16 +142,17 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '10%' }}>Jenis</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Nama</TableTh>
|
||||
<TableTh style={{ width: '12%' }}>Tanggal</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Asal/Tujuan</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>L/P</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Tanggal</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Asal/Tujuan</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>Edit</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>Hapus</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
filteredData.map((item) => {
|
||||
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
|
||||
return (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text
|
||||
@@ -164,8 +165,7 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</TableTd>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{formatTanggal(item.tanggal)}</TableTd>
|
||||
<TableTd>{item.asalTujuan}</TableTd>
|
||||
<TableTd>{item.jenisKelamin || '-'}</TableTd>
|
||||
<TableTd>{asalTujuan}</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -197,10 +197,11 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={7}>
|
||||
<TableTd colSpan={6}>
|
||||
<Center py={20}>
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada data migrasi penduduk yang cocok
|
||||
@@ -217,7 +218,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
<Box hiddenFrom="md">
|
||||
<Stack gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
filteredData.map((item) => {
|
||||
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
|
||||
return (
|
||||
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
@@ -250,10 +253,10 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Asal/Tujuan
|
||||
{item.jenis === 'MASUK' ? 'Asal' : 'Tujuan'}
|
||||
</Text>
|
||||
<Text fz="sm" fw={500} lh={1.4}>
|
||||
{item.asalTujuan}
|
||||
{asalTujuan}
|
||||
</Text>
|
||||
</Box>
|
||||
{item.alasan && (
|
||||
@@ -266,14 +269,6 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Jenis Kelamin
|
||||
</Text>
|
||||
<Text fz="sm" fw={500} lh={1.4}>
|
||||
{item.jenisKelamin || '-'}
|
||||
</Text>
|
||||
</Box>
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -304,7 +299,8 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Center py={20}>
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
|
||||
@@ -19,8 +19,8 @@ export default async function migrasiPendudukCreate(context: Context) {
|
||||
jenis: body.jenis as 'MASUK' | 'KELUAR',
|
||||
nama: body.nama,
|
||||
tanggal: new Date(body.tanggal),
|
||||
asal: isMasuk ? body.asalTujuan : undefined,
|
||||
tujuan: !isMasuk ? body.asalTujuan : undefined,
|
||||
asal: isMasuk ? body.asalTujuan : null,
|
||||
tujuan: !isMasuk ? body.asalTujuan : null,
|
||||
alasan: body.alasan,
|
||||
},
|
||||
select: {
|
||||
@@ -28,6 +28,8 @@ export default async function migrasiPendudukCreate(context: Context) {
|
||||
jenis: true,
|
||||
nama: true,
|
||||
tanggal: true,
|
||||
asal: true,
|
||||
tujuan: true,
|
||||
alasan: true,
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ const MigrasiPenduduk = new Elysia({
|
||||
tanggal: t.String(),
|
||||
asalTujuan: t.String(),
|
||||
alasan: t.Optional(t.String()),
|
||||
jenisKelamin: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.put("/:id", migrasiPendudukUpdate, {
|
||||
@@ -34,7 +33,6 @@ const MigrasiPenduduk = new Elysia({
|
||||
tanggal: t.String(),
|
||||
asalTujuan: t.String(),
|
||||
alasan: t.Optional(t.String()),
|
||||
jenisKelamin: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", migrasiPendudukDelete, {
|
||||
|
||||
@@ -38,10 +38,19 @@ export default async function migrasiPendudukUpdate(context: Context) {
|
||||
jenis: jenis as 'MASUK' | 'KELUAR',
|
||||
nama,
|
||||
tanggal: new Date(tanggal),
|
||||
asal: jenis === 'MASUK' ? asalTujuan : undefined,
|
||||
tujuan: jenis === 'KELUAR' ? asalTujuan : undefined,
|
||||
asal: jenis === 'MASUK' ? asalTujuan : null,
|
||||
tujuan: jenis === 'KELUAR' ? asalTujuan : null,
|
||||
alasan,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
jenis: true,
|
||||
nama: true,
|
||||
tanggal: true,
|
||||
asal: true,
|
||||
tujuan: true,
|
||||
alasan: true,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user