Compare commits

..

No commits in common. "6204acc6aabe69ebd74a2e2687c12ebaf03fe143" and "6b9ec0515a23c1a12855c1093a4d6e6d38093f25" have entirely different histories.

5 changed files with 226 additions and 279 deletions

View File

@ -22,7 +22,7 @@
<!-- Password -->
<div>
<label for="password" class="block text-sm font-medium">Password</label>
<InputPassword
<InputField
v-model="form.password"
id="password"
type="password"
@ -77,11 +77,10 @@
import axios from "axios";
import InputField from "@/components/InputField.vue";
import InputSelect from "@/components/InputSelect.vue";
import InputPassword from "./InputPassword.vue";
export default {
name: "CreateAkun",
components: { InputField, InputSelect, InputPassword },
components: { InputField, InputSelect },
data() {
return {
form: { nama: "", password: "", role: "" },

View File

@ -20,7 +20,7 @@
<!-- Password -->
<div>
<label for="password" class="block text-sm font-medium">Password</label>
<InputPassword
<InputField
v-model="form.password"
id="password"
type="password"
@ -31,34 +31,9 @@
<p v-if="errors.password" class="text-red-500 text-sm">{{ errors.password }}</p>
</div>
<!-- Confirm Password -->
<div v-if="form.password">
<label for="confirmPassword" class="block text-sm font-medium">Konfirmasi Password</label>
<InputPassword
v-model="form.confirmPassword"
id="confirmPassword"
type="password"
:required="false"
@input="clearError('confirmPassword')"
/>
<p v-if="errors.confirmPassword" class="text-red-500 text-sm">
{{ errors.confirmPassword }}
</p>
</div>
<!-- Role -->
<div>
<label for="role" class="block text-sm font-medium">Peran</label>
<!-- 🔒 Kalau akun sendiri tampil readonly -->
<template v-if="isEditingSelf">
<p class="mt-1 px-3 py-2 border rounded bg-gray-100 text-gray-700">
{{ form.role === 'owner' ? 'Owner' : 'Kasir' }}
</p>
</template>
<!-- 🔓 Kalau akun lain bisa diubah -->
<template v-else>
<InputSelect
v-model="form.role"
:options="[
@ -69,7 +44,6 @@
@change="clearError('role')"
/>
<p v-if="errors.role" class="text-red-500 text-sm">{{ errors.role }}</p>
</template>
</div>
<!-- Tombol -->
@ -83,8 +57,7 @@
</button>
<button
type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="!isFormValid"
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"
>
Ubah
</button>
@ -97,114 +70,105 @@
</p>
</div>
</div>
</template>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import axios from "axios";
import InputField from "@/components/InputField.vue";
import InputSelect from "@/components/InputSelect.vue";
import InputPassword from "./InputPassword.vue";
<script>
import axios from "axios";
import InputField from "@/components/InputField.vue";
import InputSelect from "@/components/InputSelect.vue";
const props = defineProps({
export default {
name: "EditAkun",
props: {
akun: {
type: Object,
required: true,
},
});
const emit = defineEmits(["refresh", "close"]);
const form = ref({
nama: props.akun?.nama || "",
},
components: { InputField, InputSelect },
data() {
return {
form: {
nama: this.akun?.nama || "",
password: "",
confirmPassword: "",
role: props.akun?.role || "",
});
const errors = ref({ nama: "", password: "", confirmPassword: "", role: "" });
const errorMessage = ref("");
const loggedInId = ref(localStorage.getItem("userId")); // 🔥 ambil dari localStorage
const isFormValid = computed(() => {
if (form.value.password && form.value.password !== form.value.confirmPassword) {
return false;
}
return (
form.value.nama.trim() &&
form.value.role &&
!errors.value.nama &&
!errors.value.password &&
!errors.value.confirmPassword &&
!errors.value.role
);
});
// 🔥 ini cek apakah akun yang diedit adalah akun sendiri
const isEditingSelf = computed(() => {
return String(props.akun.id) === String(loggedInId.value);
});
const clearError = (field) => {
errors.value[field] = "";
errorMessage.value = "";
role: this.akun?.role || "",
},
errors: { nama: "", password: "", role: "" },
errorMessage: "",
};
const validateForm = () => {
},
watch: {
akun: {
handler(newVal) {
if (newVal) {
this.form = {
nama: newVal.nama || "",
password: "",
role: newVal.role || "",
};
this.errors = { nama: "", password: "", role: "" };
this.errorMessage = "";
}
},
deep: true,
immediate: true,
},
},
methods: {
clearError(field) {
this.errors[field] = "";
this.errorMessage = "";
},
validateForm() {
let valid = true;
errors.value = { nama: "", password: "", confirmPassword: "", role: "" };
this.errors = { nama: "", password: "", role: "" };
if (!form.value.nama) {
errors.value.nama = "Nama wajib diisi";
if (!this.form.nama) {
this.errors.nama = "Nama wajib diisi";
valid = false;
}
if (form.value.password && form.value.password.length < 6) {
errors.value.password = "Password minimal 6 karakter";
if (this.form.password && this.form.password.length < 6) {
this.errors.password = "Password minimal 6 karakter";
valid = false;
}
if (form.value.password && form.value.password !== form.value.confirmPassword) {
errors.value.confirmPassword = "Konfirmasi password tidak cocok";
if (!this.form.role) {
this.errors.role = "Role wajib dipilih";
valid = false;
}
if (!form.value.role) {
errors.value.role = "Role wajib dipilih";
} else if (!["owner", "kasir"].includes(this.form.role)) {
this.errors.role = "Role harus owner atau kasir";
valid = false;
}
return valid;
};
const updateAkun = async () => {
if (!validateForm()) return;
},
async updateAkun() {
if (!this.validateForm()) return;
try {
const payload = { ...form.value };
const payload = { ...this.form };
if (!payload.password) delete payload.password;
delete payload.confirmPassword;
await axios.put(`/api/user/${props.akun.id}`, payload, {
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
await axios.put(`/api/user/${this.akun.id}`, payload, {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
emit("refresh");
emit("close");
this.$emit("refresh");
this.$emit("close");
} catch (err) {
if (err.response?.status === 422 && err.response.data.errors) {
const backendErrors = err.response.data.errors;
Object.keys(backendErrors).forEach((key) => {
errors.value[key] = backendErrors[key][0];
this.errors[key] = backendErrors[key][0];
});
} else {
errorMessage.value = err.response?.data?.message || "Gagal update akun.";
this.errorMessage =
err.response?.data?.message || "Gagal update akun.";
}
console.error("Gagal update akun:", err);
}
};
onMounted(() => {
console.log("Akun.id:", props.akun.id);
console.log("LoggedInId:", loggedInId.value);
console.log("isEditingSelf:", isEditingSelf.value);
});
</script>
},
},
};
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="relative mb-1">
<div class="relative mb-8">
<input
:type="showPassword ? 'text' : 'password'"
:value="modelValue"

View File

@ -51,14 +51,14 @@
<!-- Table Section -->
<div
class="bg-white rounded-lg shadow-md border border-D overflow-hidden"
class="bg-white rounded-lg shadow-md border border-C overflow-hidden"
>
<table class="w-full">
<thead>
<tr class="bg-C text-white">
<th class="px-6 py-4 text-center text-D border-r border-D">No</th>
<th class="px-6 py-4 text-center text-D border-r border-D">Nama</th>
<th class="px-6 py-4 text-center text-D border-r border-D">Peran</th>
<th class="px-6 py-4 text-center text-D border-r border-C">No</th>
<th class="px-6 py-4 text-center text-D border-r border-C">Nama</th>
<th class="px-6 py-4 text-center text-D border-r border-C">Peran</th>
<th class="px-6 py-4 text-center text-D">Aksi</th>
</tr>
</thead>
@ -66,16 +66,16 @@
<tr
v-for="(item, index) in akun"
:key="item.id"
class="border-b border-D hover:bg-gray-50 transition duration-150"
class="border-b border-C hover:bg-gray-50 transition duration-150"
:class="{ 'bg-gray-50': index % 2 === 1 }"
>
<td class="px-6 py-4 border-r border-D text-center font-medium text-gray-900">
<td class="px-6 py-4 border-r border-C text-center font-medium text-gray-900">
{{ index + 1 }}
</td>
<td class="px-6 py-4 border-r border-D text-D">
<td class="px-6 py-4 border-r border-C text-D">
{{ item.nama }}
</td>
<td class="px-6 py-4 border-r border-D text-gray-800">
<td class="px-6 py-4 border-r border-C text-gray-800">
{{ item.role }}
</td>
<td class="px-6 py-4 text-center">

View File

@ -14,49 +14,39 @@
placeholder="Username"
class="mb-4"
/>
<InputPassword v-model="password" placeholder="Password" />
<div
v-if="errorMessage"
class="mt-2 text-red-500 text-xs font-medium text-left"
>
{{ errorMessage }}
</div>
<PasswordInput v-model="password" placeholder="Password" />
</div>
<!-- Button -->
<button
@click="handleLogin"
:disabled="loading"
class="w-full mt-6 py-2 bg-sky-400 hover:bg-sky-500 rounded font-bold text-gray-800 transition disabled:opacity-50"
class="w-full py-2 bg-sky-400 hover:bg-sky-500 rounded font-bold text-gray-800 transition disabled:opacity-50"
>
{{ loading ? "Loading..." : "Login" }}
</button>
</div>
</div>
</template>
</template>
<script setup>
import { ref } from "vue";
import logo from "@/../images/logo.png";
import logo from '@/../images/logo.png'
import InputField from "@/components/InputField.vue";
import InputPassword from "@/components/InputPassword.vue";
import PasswordInput from "@/components/InputPassword.vue";
import axios from "axios";
const username = ref("");
const password = ref("");
const loading = ref(false);
const errorMessage = ref("");
const handleLogin = async () => {
if (!username.value || !password.value) {
errorMessage.value = "Harap isi username dan password!";
alert("Harap isi username dan password!");
return;
}
loading.value = true;
errorMessage.value = "";
try {
const res = await axios.post("/api/login", {
nama: username.value,
@ -68,19 +58,13 @@ const handleLogin = async () => {
// Simpan token & role
localStorage.setItem("token", data.token);
localStorage.setItem("role", data.role);
localStorage.setItem("userId", data.user.id);
localStorage.setItem("nama", data.user.nama);
localStorage.setItem("role", data.user.role);
// Redirect sesuai role
window.location.href = data.redirect;
} catch (error) {
if (error.response?.data?.message) {
errorMessage.value = error.response.data.message;
} else {
errorMessage.value = "Login gagal. Periksa username atau password.";
}
console.error(error);
alert("Login gagal. Periksa username atau password.");
} finally {
loading.value = false;
}