vue3plate-auth-sanctum
Version:
Vue 3 authentication package for vue3plate boilerplate that integrates with Laravel Sanctum
1,122 lines (1,121 loc) • 33.5 kB
JavaScript
import { computed as g, ref as R, resolveComponent as y, openBlock as d, createElementBlock as u, withModifiers as O, createElementVNode as s, createVNode as l, createTextVNode as _, toDisplayString as p, createCommentVNode as h, withDirectives as b, normalizeClass as v, vModelText as S, vModelDynamic as P, vModelCheckbox as D, createBlock as N, withCtx as T, renderSlot as X } from "vue";
import { useStore as Q } from "vuex";
import { useRouter as Z } from "vue-router";
import $ from "axios";
function I(a) {
const e = Q(), r = Z(), o = g(() => e.getters["auth/isAuthenticated"]), n = g(() => e.getters["auth/user"]), m = g(() => e.getters["auth/token"]), t = g(() => e.getters["auth/isLoading"]), c = g(() => e.getters["auth/error"]), i = g(() => e.getters["auth/userRole"]), L = g(() => e.getters["auth/userStatus"]), A = g(() => e.getters["auth/isAdmin"]), k = g(() => e.getters["auth/isStaff"]), M = g(() => e.getters["auth/isActive"]), H = g(() => e.getters["auth/fullName"]), j = async (f) => {
try {
await e.dispatch("auth/login", f), r.push(a.afterLogin);
} catch (x) {
throw x;
}
}, B = async (f) => {
try {
await e.dispatch("auth/register", f), r.push(a.afterRegister);
} catch (x) {
throw x;
}
}, z = async () => {
try {
await e.dispatch("auth/logout"), r.push(a.afterLogout);
} catch (f) {
console.warn("Logout error:", f), r.push(a.afterLogout);
}
}, J = () => {
e.dispatch("auth/clearError");
}, W = () => {
e.dispatch("auth/initializeAuth");
}, q = (f) => i.value === f, U = (f) => f.includes(i.value), Y = (f) => !!(A.value || k.value && ["read", "update"].includes(f)), C = () => o.value ? !0 : (r.push("/login"), !1);
return {
// State
isAuthenticated: o,
user: n,
token: m,
isLoading: t,
error: c,
userRole: i,
userStatus: L,
isAdmin: A,
isStaff: k,
isActive: M,
fullName: H,
// Actions
login: j,
register: B,
logout: z,
clearError: J,
initializeAuth: W,
// Utilities
hasRole: q,
hasAnyRole: U,
can: Y,
requireAuth: C,
requireGuest: () => o.value ? (r.push(a.afterLogin), !1) : !0,
requireRole: (f) => C() ? q(f) ? !0 : (r.push("/unauthorized"), !1) : !1,
requireAnyRole: (f) => C() ? U(f) ? !0 : (r.push("/unauthorized"), !1) : !1
};
}
const E = (a, e) => {
const r = a.__vccOpts || a;
for (const [o, n] of e)
r[o] = n;
return r;
}, ee = {
name: "LoginForm",
props: {
config: {
type: Object,
default: () => ({})
}
},
emits: ["success", "error"],
setup(a, { emit: e }) {
const { login: r, isLoading: o, error: n, clearError: m } = I(a.config), t = R({
email: "",
password: "",
remember: !1
}), c = R(!1), i = g(() => {
var A;
return ((A = n.value) == null ? void 0 : A.errors) || {};
});
return {
form: t,
showPassword: c,
errors: i,
isLoading: o,
error: n,
handleSubmit: async () => {
m();
try {
await r({
email: t.value.email,
password: t.value.password
}), e("success");
} catch (A) {
e("error", A);
}
}
};
}
}, oe = {
key: 0,
class: "error-message"
}, re = { class: "form-group" }, se = { class: "input-group" }, te = {
key: 0,
class: "field-error"
}, ne = { class: "form-group" }, ae = { class: "input-group" }, ie = ["type"], le = {
key: 0,
class: "field-error"
}, de = { class: "form-group" }, ue = { class: "checkbox-label" }, ce = ["disabled"], me = { class: "form-footer" }, fe = { class: "signup-prompt" };
function ge(a, e, r, o, n, m) {
const t = y("font-awesome-icon"), c = y("router-link");
return d(), u("form", {
onSubmit: e[4] || (e[4] = O((...i) => o.handleSubmit && o.handleSubmit(...i), ["prevent"])),
class: "login-form"
}, [
e[12] || (e[12] = s("div", { class: "form-header" }, [
s("h2", { class: "form-title" }, "Welcome Back"),
s("p", { class: "form-subtitle" }, "Sign in to your account")
], -1)),
o.error ? (d(), u("div", oe, [
l(t, { icon: "fa-solid fa-exclamation-circle" }),
_(" " + p(o.error.message), 1)
])) : h("", !0),
s("div", re, [
e[5] || (e[5] = s("label", {
for: "email",
class: "form-label"
}, "Email Address", -1)),
s("div", se, [
l(t, {
icon: "fa-solid fa-envelope",
class: "input-icon"
}),
b(s("input", {
id: "email",
"onUpdate:modelValue": e[0] || (e[0] = (i) => o.form.email = i),
type: "email",
class: v(["form-input", { error: o.errors.email }]),
placeholder: "Enter your email",
required: "",
autocomplete: "email"
}, null, 2), [
[S, o.form.email]
])
]),
o.errors.email ? (d(), u("span", te, p(o.errors.email[0]), 1)) : h("", !0)
]),
s("div", ne, [
e[6] || (e[6] = s("label", {
for: "password",
class: "form-label"
}, "Password", -1)),
s("div", ae, [
l(t, {
icon: "fa-solid fa-lock",
class: "input-icon"
}),
b(s("input", {
id: "password",
"onUpdate:modelValue": e[1] || (e[1] = (i) => o.form.password = i),
type: o.showPassword ? "text" : "password",
class: v(["form-input", { error: o.errors.password }]),
placeholder: "Enter your password",
required: "",
autocomplete: "current-password"
}, null, 10, ie), [
[P, o.form.password]
]),
s("button", {
type: "button",
onClick: e[2] || (e[2] = (i) => o.showPassword = !o.showPassword),
class: "password-toggle"
}, [
l(t, {
icon: o.showPassword ? "fa-solid fa-eye-slash" : "fa-solid fa-eye"
}, null, 8, ["icon"])
])
]),
o.errors.password ? (d(), u("span", le, p(o.errors.password[0]), 1)) : h("", !0)
]),
s("div", de, [
s("label", ue, [
b(s("input", {
"onUpdate:modelValue": e[3] || (e[3] = (i) => o.form.remember = i),
type: "checkbox",
class: "checkbox-input"
}, null, 512), [
[D, o.form.remember]
]),
e[7] || (e[7] = s("span", { class: "checkbox-custom" }, null, -1)),
e[8] || (e[8] = _(" Remember me "))
])
]),
s("button", {
type: "submit",
class: "submit-button",
disabled: o.isLoading
}, [
o.isLoading ? (d(), N(t, {
key: 0,
icon: "fa-solid fa-spinner",
spin: "",
class: "button-icon"
})) : (d(), N(t, {
key: 1,
icon: "fa-solid fa-sign-in-alt",
class: "button-icon"
})),
_(" " + p(o.isLoading ? "Signing In..." : "Sign In"), 1)
], 8, ce),
s("div", me, [
l(c, {
to: "/forgot-password",
class: "forgot-link"
}, {
default: T(() => e[9] || (e[9] = [
_(" Forgot your password? ")
])),
_: 1,
__: [9]
}),
s("div", fe, [
e[11] || (e[11] = _(" Don't have an account? ")),
l(c, {
to: "/register",
class: "signup-link"
}, {
default: T(() => e[10] || (e[10] = [
_(" Sign up here ")
])),
_: 1,
__: [10]
})
])
])
], 32);
}
const V = /* @__PURE__ */ E(ee, [["render", ge], ["__scopeId", "data-v-de152c65"]]);
const pe = {
name: "LoginPage",
components: {
LoginForm: V
},
props: {
config: {
type: Object,
default: () => ({})
}
},
methods: {
handleLoginSuccess() {
console.log("Login successful");
},
handleLoginError(a) {
console.error("Login error:", a);
}
}
}, _e = { class: "login-page" }, he = { class: "login-container" }, be = { class: "login-content" }, we = { class: "brand-section" }, ye = { class: "brand-logo" };
function ve(a, e, r, o, n, m) {
const t = y("font-awesome-icon"), c = y("LoginForm");
return d(), u("div", _e, [
s("div", he, [
s("div", be, [
s("div", we, [
s("div", ye, [
l(t, { icon: "fa-solid fa-sign-in-alt" })
]),
e[0] || (e[0] = s("h1", { class: "brand-title" }, "vue3plate", -1)),
e[1] || (e[1] = s("p", { class: "brand-subtitle" }, "Welcome Back", -1))
]),
l(c, {
config: r.config,
onSuccess: m.handleLoginSuccess,
onError: m.handleLoginError
}, null, 8, ["config", "onSuccess", "onError"])
])
])
]);
}
const F = /* @__PURE__ */ E(pe, [["render", ve], ["__scopeId", "data-v-999b0c89"]]);
const Ae = {
name: "RegisterForm",
props: {
config: {
type: Object,
default: () => ({})
}
},
emits: ["success", "error"],
setup(a, { emit: e }) {
const { register: r, isLoading: o, error: n, clearError: m } = I(a.config), t = R({
firstName: "",
middleName: "",
lastName: "",
email: "",
mobileNumber: "",
password: "",
passwordConfirmation: "",
agreeToTerms: !1
}), c = R(!1), i = R(!1), L = g(() => {
var k;
return ((k = n.value) == null ? void 0 : k.errors) || {};
});
return {
form: t,
showPassword: c,
showPasswordConfirm: i,
errors: L,
isLoading: o,
error: n,
handleSubmit: async () => {
m();
try {
await r({
firstName: t.value.firstName,
middleName: t.value.middleName,
lastName: t.value.lastName,
email: t.value.email,
mobileNumber: t.value.mobileNumber,
password: t.value.password,
passwordConfirmation: t.value.passwordConfirmation
}), e("success");
} catch (k) {
e("error", k);
}
}
};
}
}, ke = {
key: 0,
class: "error-message"
}, Se = { class: "form-row" }, Re = { class: "form-group" }, Ee = { class: "input-group" }, Le = {
key: 0,
class: "field-error"
}, Ne = { class: "form-group" }, Te = { class: "input-group" }, Ie = {
key: 0,
class: "field-error"
}, Ce = { class: "form-group" }, xe = { class: "input-group" }, Pe = {
key: 0,
class: "field-error"
}, qe = { class: "form-group" }, Ue = { class: "input-group" }, Oe = {
key: 0,
class: "field-error"
}, De = { class: "form-group" }, Ve = { class: "input-group" }, Fe = {
key: 0,
class: "field-error"
}, Ge = { class: "form-row" }, Ke = { class: "form-group" }, Me = { class: "input-group" }, He = ["type"], je = {
key: 0,
class: "field-error"
}, Be = { class: "form-group" }, ze = { class: "input-group" }, Je = ["type"], We = {
key: 0,
class: "field-error"
}, Ye = { class: "form-group" }, Xe = { class: "checkbox-label" }, Qe = ["disabled"], Ze = { class: "form-footer" }, $e = { class: "signin-prompt" };
function eo(a, e, r, o, n, m) {
const t = y("font-awesome-icon"), c = y("router-link");
return d(), u("form", {
onSubmit: e[10] || (e[10] = O((...i) => o.handleSubmit && o.handleSubmit(...i), ["prevent"])),
class: "register-form"
}, [
e[25] || (e[25] = s("div", { class: "form-header" }, [
s("h2", { class: "form-title" }, "Create Account"),
s("p", { class: "form-subtitle" }, "Join us today")
], -1)),
o.error ? (d(), u("div", ke, [
l(t, { icon: "fa-solid fa-exclamation-circle" }),
_(" " + p(o.error.message), 1)
])) : h("", !0),
s("div", Se, [
s("div", Re, [
e[11] || (e[11] = s("label", {
for: "firstName",
class: "form-label"
}, "First Name", -1)),
s("div", Ee, [
l(t, {
icon: "fa-solid fa-user",
class: "input-icon"
}),
b(s("input", {
id: "firstName",
"onUpdate:modelValue": e[0] || (e[0] = (i) => o.form.firstName = i),
type: "text",
class: v(["form-input", { error: o.errors.first_name }]),
placeholder: "First name",
required: "",
autocomplete: "given-name"
}, null, 2), [
[S, o.form.firstName]
])
]),
o.errors.first_name ? (d(), u("span", Le, p(o.errors.first_name[0]), 1)) : h("", !0)
]),
s("div", Ne, [
e[12] || (e[12] = s("label", {
for: "lastName",
class: "form-label"
}, "Last Name", -1)),
s("div", Te, [
l(t, {
icon: "fa-solid fa-user",
class: "input-icon"
}),
b(s("input", {
id: "lastName",
"onUpdate:modelValue": e[1] || (e[1] = (i) => o.form.lastName = i),
type: "text",
class: v(["form-input", { error: o.errors.last_name }]),
placeholder: "Last name",
required: "",
autocomplete: "family-name"
}, null, 2), [
[S, o.form.lastName]
])
]),
o.errors.last_name ? (d(), u("span", Ie, p(o.errors.last_name[0]), 1)) : h("", !0)
])
]),
s("div", Ce, [
e[13] || (e[13] = s("label", {
for: "middleName",
class: "form-label"
}, "Middle Name (Optional)", -1)),
s("div", xe, [
l(t, {
icon: "fa-solid fa-user",
class: "input-icon"
}),
b(s("input", {
id: "middleName",
"onUpdate:modelValue": e[2] || (e[2] = (i) => o.form.middleName = i),
type: "text",
class: v(["form-input", { error: o.errors.middle_name }]),
placeholder: "Middle name",
autocomplete: "additional-name"
}, null, 2), [
[S, o.form.middleName]
])
]),
o.errors.middle_name ? (d(), u("span", Pe, p(o.errors.middle_name[0]), 1)) : h("", !0)
]),
s("div", qe, [
e[14] || (e[14] = s("label", {
for: "email",
class: "form-label"
}, "Email Address", -1)),
s("div", Ue, [
l(t, {
icon: "fa-solid fa-envelope",
class: "input-icon"
}),
b(s("input", {
id: "email",
"onUpdate:modelValue": e[3] || (e[3] = (i) => o.form.email = i),
type: "email",
class: v(["form-input", { error: o.errors.email }]),
placeholder: "Enter your email",
required: "",
autocomplete: "email"
}, null, 2), [
[S, o.form.email]
])
]),
o.errors.email ? (d(), u("span", Oe, p(o.errors.email[0]), 1)) : h("", !0)
]),
s("div", De, [
e[15] || (e[15] = s("label", {
for: "mobileNumber",
class: "form-label"
}, "Mobile Number", -1)),
s("div", Ve, [
l(t, {
icon: "fa-solid fa-phone",
class: "input-icon"
}),
b(s("input", {
id: "mobileNumber",
"onUpdate:modelValue": e[4] || (e[4] = (i) => o.form.mobileNumber = i),
type: "tel",
class: v(["form-input", { error: o.errors.mobile_number }]),
placeholder: "Enter your mobile number",
required: "",
autocomplete: "tel"
}, null, 2), [
[S, o.form.mobileNumber]
])
]),
o.errors.mobile_number ? (d(), u("span", Fe, p(o.errors.mobile_number[0]), 1)) : h("", !0)
]),
s("div", Ge, [
s("div", Ke, [
e[16] || (e[16] = s("label", {
for: "password",
class: "form-label"
}, "Password", -1)),
s("div", Me, [
l(t, {
icon: "fa-solid fa-lock",
class: "input-icon"
}),
b(s("input", {
id: "password",
"onUpdate:modelValue": e[5] || (e[5] = (i) => o.form.password = i),
type: o.showPassword ? "text" : "password",
class: v(["form-input", { error: o.errors.password }]),
placeholder: "Create password",
required: "",
autocomplete: "new-password"
}, null, 10, He), [
[P, o.form.password]
]),
s("button", {
type: "button",
onClick: e[6] || (e[6] = (i) => o.showPassword = !o.showPassword),
class: "password-toggle"
}, [
l(t, {
icon: o.showPassword ? "fa-solid fa-eye-slash" : "fa-solid fa-eye"
}, null, 8, ["icon"])
])
]),
o.errors.password ? (d(), u("span", je, p(o.errors.password[0]), 1)) : h("", !0)
]),
s("div", Be, [
e[17] || (e[17] = s("label", {
for: "passwordConfirmation",
class: "form-label"
}, "Confirm Password", -1)),
s("div", ze, [
l(t, {
icon: "fa-solid fa-lock",
class: "input-icon"
}),
b(s("input", {
id: "passwordConfirmation",
"onUpdate:modelValue": e[7] || (e[7] = (i) => o.form.passwordConfirmation = i),
type: o.showPasswordConfirm ? "text" : "password",
class: v(["form-input", { error: o.errors.password_confirmation }]),
placeholder: "Confirm password",
required: "",
autocomplete: "new-password"
}, null, 10, Je), [
[P, o.form.passwordConfirmation]
]),
s("button", {
type: "button",
onClick: e[8] || (e[8] = (i) => o.showPasswordConfirm = !o.showPasswordConfirm),
class: "password-toggle"
}, [
l(t, {
icon: o.showPasswordConfirm ? "fa-solid fa-eye-slash" : "fa-solid fa-eye"
}, null, 8, ["icon"])
])
]),
o.errors.password_confirmation ? (d(), u("span", We, p(o.errors.password_confirmation[0]), 1)) : h("", !0)
])
]),
s("div", Ye, [
s("label", Xe, [
b(s("input", {
"onUpdate:modelValue": e[9] || (e[9] = (i) => o.form.agreeToTerms = i),
type: "checkbox",
class: "checkbox-input",
required: ""
}, null, 512), [
[D, o.form.agreeToTerms]
]),
e[18] || (e[18] = s("span", { class: "checkbox-custom" }, null, -1)),
e[19] || (e[19] = _(" I agree to the ")),
e[20] || (e[20] = s("a", {
href: "/terms",
target: "_blank",
class: "terms-link"
}, "Terms of Service", -1)),
e[21] || (e[21] = _(" and ")),
e[22] || (e[22] = s("a", {
href: "/privacy",
target: "_blank",
class: "terms-link"
}, "Privacy Policy", -1))
])
]),
s("button", {
type: "submit",
class: "submit-button",
disabled: o.isLoading || !o.form.agreeToTerms
}, [
o.isLoading ? (d(), N(t, {
key: 0,
icon: "fa-solid fa-spinner",
spin: "",
class: "button-icon"
})) : (d(), N(t, {
key: 1,
icon: "fa-solid fa-user-plus",
class: "button-icon"
})),
_(" " + p(o.isLoading ? "Creating Account..." : "Create Account"), 1)
], 8, Qe),
s("div", Ze, [
s("div", $e, [
e[24] || (e[24] = _(" Already have an account? ")),
l(c, {
to: "/login",
class: "signin-link"
}, {
default: T(() => e[23] || (e[23] = [
_(" Sign in here ")
])),
_: 1,
__: [23]
})
])
])
], 32);
}
const G = /* @__PURE__ */ E(Ae, [["render", eo], ["__scopeId", "data-v-be35a40f"]]);
const oo = {
name: "RegisterPage",
components: {
RegisterForm: G
},
props: {
config: {
type: Object,
default: () => ({})
}
},
methods: {
handleRegisterSuccess() {
console.log("Registration successful");
},
handleRegisterError(a) {
console.error("Registration error:", a);
}
}
}, ro = { class: "register-page" }, so = { class: "register-container" }, to = { class: "register-content" }, no = { class: "brand-section" }, ao = { class: "brand-logo" };
function io(a, e, r, o, n, m) {
const t = y("font-awesome-icon"), c = y("RegisterForm");
return d(), u("div", ro, [
s("div", so, [
s("div", to, [
s("div", no, [
s("div", ao, [
l(t, { icon: "fa-solid fa-user-plus" })
]),
e[0] || (e[0] = s("h1", { class: "brand-title" }, "vue3plate", -1)),
e[1] || (e[1] = s("p", { class: "brand-subtitle" }, "Join Our Community", -1))
]),
l(c, {
config: r.config,
onSuccess: m.handleRegisterSuccess,
onError: m.handleRegisterError
}, null, 8, ["config", "onSuccess", "onError"])
])
])
]);
}
const K = /* @__PURE__ */ E(oo, [["render", io], ["__scopeId", "data-v-ea5bb4e2"]]);
const lo = {
name: "AuthGuard",
props: {
config: {
type: Object,
default: () => ({})
},
requireRole: {
type: String,
default: null
},
requireAnyRole: {
type: Array,
default: () => []
}
},
setup(a) {
const { isAuthenticated: e, isLoading: r, userRole: o, hasRole: n, hasAnyRole: m } = I(a.config), t = g(() => a.requireRole ? n(a.requireRole) : a.requireAnyRole.length > 0 ? m(a.requireAnyRole) : !0);
return {
isAuthenticated: g(() => e.value && t.value),
isLoading: r,
userRole: o
};
}
}, uo = { key: 0 }, co = {
key: 1,
class: "auth-guard-message"
}, mo = { class: "message-content" }, fo = {
key: 2,
class: "auth-guard-loading"
};
function go(a, e, r, o, n, m) {
const t = y("font-awesome-icon"), c = y("router-link");
return o.isAuthenticated ? (d(), u("div", uo, [
X(a.$slots, "default", {}, void 0, !0)
])) : o.isLoading ? (d(), u("div", fo, [
l(t, {
icon: "fa-solid fa-spinner",
spin: "",
class: "loading-icon"
}),
e[3] || (e[3] = s("p", null, "Checking authentication...", -1))
])) : (d(), u("div", co, [
s("div", mo, [
l(t, {
icon: "fa-solid fa-lock",
class: "lock-icon"
}),
e[1] || (e[1] = s("h3", { class: "message-title" }, "Authentication Required", -1)),
e[2] || (e[2] = s("p", { class: "message-text" }, "You need to be logged in to access this content.", -1)),
l(c, {
to: "/login",
class: "login-button"
}, {
default: T(() => [
l(t, { icon: "fa-solid fa-sign-in-alt" }),
e[0] || (e[0] = _(" Sign In "))
]),
_: 1,
__: [0]
})
])
]));
}
const po = /* @__PURE__ */ E(lo, [["render", go], ["__scopeId", "data-v-f42a8912"]]);
class _o {
constructor() {
this.config = null, this.client = null;
}
/**
* Initialize the API client with configuration
* @param {Object} config - Configuration object
*/
init(e) {
this.config = e, this.client = $.create({
baseURL: e.apiBaseUrl,
timeout: 1e4,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest"
}
}), this.client.interceptors.request.use(
(r) => {
const o = localStorage.getItem(e.tokenKey);
return o && (r.headers.Authorization = `Bearer ${o}`), r;
},
(r) => Promise.reject(r)
), this.client.interceptors.response.use(
(r) => r,
(r) => {
var o;
return ((o = r.response) == null ? void 0 : o.status) === 401 && (this.clearAuthData(), window.location.pathname !== "/login" && (window.location.href = "/login")), Promise.reject(r);
}
);
}
/**
* Register a new user account
* @param {Object} userData - User registration data
* @returns {Promise} API response
*/
async register(e) {
try {
const r = await this.client.post("/register-account", {
first_name: e.firstName,
middle_name: e.middleName || "",
last_name: e.lastName,
email: e.email,
mobile_number: e.mobileNumber,
password: e.password,
password_confirmation: e.passwordConfirmation
});
return r.data.token && this.setAuthData(r.data.token, r.data.user), r.data;
} catch (r) {
throw this.handleError(r);
}
}
/**
* Login user
* @param {Object} credentials - Login credentials
* @returns {Promise} API response
*/
async login(e) {
try {
const r = await this.client.post("/login", {
email: e.email,
password: e.password
});
return r.data.token && this.setAuthData(r.data.token, r.data.user), r.data;
} catch (r) {
throw this.handleError(r);
}
}
/**
* Logout user
* @returns {Promise} API response
*/
async logout() {
try {
const e = await this.client.post("/logout");
return this.clearAuthData(), e.data;
} catch (e) {
throw this.clearAuthData(), this.handleError(e);
}
}
/**
* Set authentication data in localStorage
* @param {string} token - Auth token
* @param {Object} user - User data
*/
setAuthData(e, r) {
localStorage.setItem(this.config.tokenKey, e), localStorage.setItem(this.config.userKey, JSON.stringify(r));
}
/**
* Clear authentication data from localStorage
*/
clearAuthData() {
localStorage.removeItem(this.config.tokenKey), localStorage.removeItem(this.config.userKey);
}
/**
* Get stored token
* @returns {string|null} Auth token
*/
getStoredToken() {
return localStorage.getItem(this.config.tokenKey);
}
/**
* Get stored user data
* @returns {Object|null} User data
*/
getStoredUser() {
const e = localStorage.getItem(this.config.userKey);
return e ? JSON.parse(e) : null;
}
/**
* Check if user is authenticated
* @returns {boolean} Authentication status
*/
isAuthenticated() {
return !!this.getStoredToken();
}
/**
* Handle API errors
* @param {Error} error - Axios error
* @returns {Object} Formatted error
*/
handleError(e) {
return e.response ? {
message: e.response.data.message || "An error occurred",
errors: e.response.data.errors || {},
status: e.response.status
} : e.request ? {
message: "Network error. Please check your connection.",
errors: {},
status: 0
} : {
message: e.message || "An unexpected error occurred",
errors: {},
status: 0
};
}
}
const w = new _o();
function ho(a) {
return {
namespaced: !0,
state: () => ({
user: w.getStoredUser(),
token: w.getStoredToken(),
isLoading: !1,
error: null,
isAuthenticated: w.isAuthenticated()
}),
getters: {
isAuthenticated: (e) => e.isAuthenticated,
user: (e) => e.user,
token: (e) => e.token,
isLoading: (e) => e.isLoading,
error: (e) => e.error,
userRole: (e) => {
var r, o;
return ((o = (r = e.user) == null ? void 0 : r.role) == null ? void 0 : o.name) || null;
},
userStatus: (e) => {
var r, o;
return ((o = (r = e.user) == null ? void 0 : r.status) == null ? void 0 : o.name) || null;
},
isAdmin: (e) => {
var r, o;
return ((o = (r = e.user) == null ? void 0 : r.role) == null ? void 0 : o.name) === "Admin";
},
isStaff: (e) => {
var r, o;
return ((o = (r = e.user) == null ? void 0 : r.role) == null ? void 0 : o.name) === "Staff";
},
isActive: (e) => {
var r, o;
return ((o = (r = e.user) == null ? void 0 : r.status) == null ? void 0 : o.name) === "Active";
},
fullName: (e) => {
if (!e.user)
return "";
const { first_name: r, middle_name: o, last_name: n } = e.user;
return [r, o, n].filter(Boolean).join(" ");
}
},
mutations: {
SET_LOADING(e, r) {
e.isLoading = r;
},
SET_ERROR(e, r) {
e.error = r;
},
SET_AUTH_DATA(e, { user: r, token: o }) {
e.user = r, e.token = o, e.isAuthenticated = !0, e.error = null;
},
CLEAR_AUTH_DATA(e) {
e.user = null, e.token = null, e.isAuthenticated = !1, e.error = null;
},
UPDATE_USER(e, r) {
e.user && (e.user = { ...e.user, ...r });
}
},
actions: {
/**
* Login user
* @param {Object} context - Vuex context
* @param {Object} credentials - Login credentials
*/
async login({ commit: e, dispatch: r }, o) {
e("SET_LOADING", !0), e("SET_ERROR", null);
try {
const n = await w.login(o);
return e("SET_AUTH_DATA", {
user: n.user,
token: n.token
}), r("app/addHeaderMenuItem", {
id: "user-menu",
type: "dropdown",
label: n.user.first_name,
icon: "fa-solid fa-user",
items: [
{
id: "logout",
label: "Logout",
icon: "fa-solid fa-sign-out-alt",
action: () => r("logout")
}
]
}, { root: !0 }), n;
} catch (n) {
throw e("SET_ERROR", n), n;
} finally {
e("SET_LOADING", !1);
}
},
/**
* Register new user
* @param {Object} context - Vuex context
* @param {Object} userData - Registration data
*/
async register({ commit: e, dispatch: r }, o) {
e("SET_LOADING", !0), e("SET_ERROR", null);
try {
const n = await w.register(o);
return e("SET_AUTH_DATA", {
user: n.user,
token: n.token
}), r("app/addHeaderMenuItem", {
id: "user-menu",
type: "dropdown",
label: n.user.first_name,
icon: "fa-solid fa-user",
items: [
{
id: "logout",
label: "Logout",
icon: "fa-solid fa-sign-out-alt",
action: () => r("logout")
}
]
}, { root: !0 }), n;
} catch (n) {
throw e("SET_ERROR", n), n;
} finally {
e("SET_LOADING", !1);
}
},
/**
* Logout user
* @param {Object} context - Vuex context
*/
async logout({ commit: e, dispatch: r }) {
e("SET_LOADING", !0);
try {
await w.logout();
} catch (o) {
console.warn("Logout API call failed:", o);
} finally {
e("CLEAR_AUTH_DATA"), r("app/removeHeaderMenuItem", "user-menu", { root: !0 }), e("SET_LOADING", !1);
}
},
/**
* Initialize auth state from localStorage
* @param {Object} context - Vuex context
*/
initializeAuth({ commit: e, dispatch: r }) {
const o = w.getStoredToken(), n = w.getStoredUser();
o && n && (e("SET_AUTH_DATA", { user: n, token: o }), r("app/addHeaderMenuItem", {
id: "user-menu",
type: "dropdown",
label: n.first_name,
icon: "fa-solid fa-user",
items: [
{
id: "logout",
label: "Logout",
icon: "fa-solid fa-sign-out-alt",
action: () => r("logout")
}
]
}, { root: !0 }));
},
/**
* Clear error state
* @param {Object} context - Vuex context
*/
clearError({ commit: e }) {
e("SET_ERROR", null);
}
}
};
}
function bo(a) {
return [
{
path: "/login",
name: "Login",
component: F,
meta: {
layout: "auth",
requiresGuest: !0,
title: "Login"
},
beforeEnter: (e, r, o) => {
localStorage.getItem(a.tokenKey) ? o(a.afterLogin) : o();
}
},
{
path: "/register",
name: "Register",
component: K,
meta: {
layout: "auth",
requiresGuest: !0,
title: "Register"
},
beforeEnter: (e, r, o) => {
localStorage.getItem(a.tokenKey) ? o(a.afterRegister) : o();
}
}
];
}
function Eo(a = {}) {
const r = { ...{
apiBaseUrl: {}.VITE_API_URL || "http://localhost:8000/api",
tokenKey: {}.VITE_TOKEN_KEY || "auth_token",
userKey: {}.VITE_USER_KEY || "auth_user",
afterLogin: "/dashboard",
afterLogout: "/",
afterRegister: "/dashboard"
}, ...a };
return w.init(r), {
name: "authentication",
config: r,
// Routes to register
routes: bo(r),
// Store module to register
store: ho(),
// Components to register globally (optional)
components: {
LoginForm: V,
RegisterForm: G,
AuthGuard: po
},
// Views (for manual route registration)
views: {
LoginPage: F,
RegisterPage: K
},
// Composables
composables: {
useAuth: () => I(r)
},
// API utilities
api: w,
// Sidebar menu items to add
sidebarItems: [],
// Header menu items to add
headerItems: [
{
id: "auth-logout",
label: "Logout",
icon: "fa-solid fa-sign-out-alt",
action: "logout",
requiresAuth: !0
}
]
};
}
export {
po as AuthGuard,
V as LoginForm,
F as LoginPage,
G as RegisterForm,
K as RegisterPage,
w as authApi,
bo as authRoutes,
ho as authStore,
Eo as createAuthenticationFeature,
Eo as default,
I as useAuth
};