feat: implement user profile management with avatars, login, and personal data
- Add avatar upload functionality with public URL access. - Implement user login with email and password authentication. - Create endpoints for fetching and updating the authenticated user's profile. - Set up database migrations for user profiles, including email and role management. - Introduce personal data management linked to user profiles. - Add email verification process with welcome email templates.
This commit is contained in:
150
frontend/src/stores/admin-store.ts
Normal file
150
frontend/src/stores/admin-store.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import Client, { Local, admin as AdminNS, auth as AuthNS, profiles as ProfilesNS } from '@/encore/client';
|
||||
import { useProfilesStore } from '@/stores/profiles-store';
|
||||
|
||||
export type Profile = AdminNS.Profile;
|
||||
export type ProfileParams = AdminNS.ProfileParams;
|
||||
export type RegisterParams = AdminNS.RegisterParams;
|
||||
export type PersonalData = AdminNS.PersonalData;
|
||||
export type PersonalDataParams = AdminNS.PersonalDataParams;
|
||||
export type RoleOption = AuthNS.RoleOption;
|
||||
export type StatusOption = ProfilesNS.StatusOption;
|
||||
|
||||
export const useAdminStore = defineStore('admin', () => {
|
||||
const profiles = ref<Profile[]>([]);
|
||||
const roles = ref<RoleOption[]>([]);
|
||||
const statuses = ref<StatusOption[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
const profilesStore = useProfilesStore();
|
||||
|
||||
const client = new Client(Local, {
|
||||
auth: () => (profilesStore.token ? { Authorization: `Bearer ${profilesStore.token}` } : undefined),
|
||||
});
|
||||
|
||||
async function withLoading<T>(fn: () => Promise<T>): Promise<T> {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : String(err);
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetches all profiles into `profiles`. Requires the caller to be logged in as an admin. */
|
||||
async function listProfiles(): Promise<Profile[]> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.ListProfiles();
|
||||
profiles.value = res.profiles;
|
||||
return res.profiles;
|
||||
});
|
||||
}
|
||||
|
||||
/** Creates a new user profile with credentials. Requires the caller to be logged in as an admin. */
|
||||
async function insertProfile(params: RegisterParams): Promise<Profile> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.InsertProfile(params);
|
||||
profiles.value.push(res);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
/** Fetches the valid roles and profile statuses into `roles` and `statuses`. */
|
||||
async function fetchSystemOptions(): Promise<void> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.GetSystemOptions();
|
||||
roles.value = res.roles;
|
||||
statuses.value = res.statuses;
|
||||
});
|
||||
}
|
||||
|
||||
/** Statuses that an admin can manually set on a profile. */
|
||||
const updatableStatuses = computed(() => statuses.value.filter((s) => s.updatable));
|
||||
|
||||
function replaceProfile(updated: Profile) {
|
||||
const idx = profiles.value.findIndex((p) => p.user_id === updated.user_id);
|
||||
if (idx !== -1) {
|
||||
profiles.value[idx] = updated;
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates any user's profile. Requires the caller to be logged in as an admin. */
|
||||
async function updateProfile(userID: string, params: ProfileParams): Promise<Profile> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.UpdateProfile(userID, params);
|
||||
replaceProfile(res);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
/** Updates the role of any user's profile. */
|
||||
async function updateProfileRole(userID: string, role: string): Promise<Profile> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.UpdateProfileRole(userID, { role });
|
||||
replaceProfile(res);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
/** Updates the status of any user's profile. */
|
||||
async function updateProfileStatus(userID: string, status: ProfilesNS.Status): Promise<Profile> {
|
||||
return withLoading(async () => {
|
||||
const res = await client.admin.UpdateProfileStatus(userID, { status });
|
||||
replaceProfile(res);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
/** Fetches the personal data of any user. Requires the caller to be logged in as an admin. */
|
||||
async function getPersonalData(userID: string): Promise<PersonalData> {
|
||||
return withLoading(async () => {
|
||||
return await client.admin.GetPersonalData(userID);
|
||||
});
|
||||
}
|
||||
|
||||
/** Creates or replaces the personal data of any user. */
|
||||
async function upsertPersonalData(userID: string, params: PersonalDataParams): Promise<PersonalData> {
|
||||
return withLoading(async () => {
|
||||
return await client.admin.UpsertPersonalData(userID, params);
|
||||
});
|
||||
}
|
||||
|
||||
/** Uploads an avatar image and returns its public URL. */
|
||||
async function uploadAvatar(image: Blob): Promise<string> {
|
||||
return withLoading(async () => {
|
||||
const resp = await client.profiles.UploadAvatar('POST', image);
|
||||
if (!resp.ok) {
|
||||
throw new Error(`avatar upload failed (${resp.status})`);
|
||||
}
|
||||
const data = (await resp.json()) as { url: string };
|
||||
return data.url;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
// state
|
||||
profiles,
|
||||
roles,
|
||||
statuses,
|
||||
loading,
|
||||
error,
|
||||
// getters
|
||||
updatableStatuses,
|
||||
// actions
|
||||
listProfiles,
|
||||
fetchSystemOptions,
|
||||
insertProfile,
|
||||
updateProfile,
|
||||
updateProfileRole,
|
||||
updateProfileStatus,
|
||||
getPersonalData,
|
||||
upsertPersonalData,
|
||||
uploadAvatar,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user