관리자 기능과 계정 즐겨찾기 개선

This commit is contained in:
2026-06-23 14:32:43 +09:00
parent 00e05dbe3f
commit 93e81ce3ea
26 changed files with 804 additions and 914 deletions

View File

@@ -165,6 +165,23 @@ const UaDeckApi = (() => {
getPresets: () => request('/decks/presets'),
getFavorites: () => request('/decks/favorites'),
/** @param {string[]} shareCodes */
syncFavorites: (shareCodes) =>
request('/decks/favorites', {
method: 'POST',
body: JSON.stringify({ shareCodes }),
}),
/** @param {string} shareCode */
addFavorite: (shareCode) =>
request(`/decks/favorites/${shareCode}`, { method: 'PUT' }),
/** @param {string} shareCode */
removeFavorite: (shareCode) =>
request(`/decks/favorites/${shareCode}`, { method: 'DELETE' }),
/** 관리자: 회원 목록 */
adminGetUsers: (q = '', page = 1) =>
request(`/admin/users?q=${encodeURIComponent(q)}&page=${page}`),
@@ -185,6 +202,11 @@ const UaDeckApi = (() => {
adminGetDecks: () => request('/admin/decks'),
adminGetOrphanPreviews: () => request('/admin/previews/orphans'),
adminDeleteOrphanPreviews: () =>
request('/admin/previews/orphans', { method: 'DELETE' }),
/** @param {object} body */
adminCreatePreset: (body) =>
request('/admin/decks/preset', { method: 'POST', body: JSON.stringify(body) }),

View File

@@ -1,13 +1,19 @@
/**
* 덱 즐겨찾기 (localStorage, share_code 기준)
* 덱 즐겨찾기
* 로그인 사용자는 계정 DB, 비로그인 사용자는 localStorage를 사용한다.
*/
(function initDeckFavorites() {
const STORAGE_KEY = 'uadeck.deckFavorites';
/** @type {Set<string>} */
let favorites = new Set();
/** @type {Promise<void>|null} */
let initPromise = null;
/**
* 브라우저 즐겨찾기 읽기
* @returns {Set<string>}
*/
function readSet() {
function readLocalSet() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const list = raw ? JSON.parse(raw) : [];
@@ -18,43 +24,87 @@
}
/**
* 브라우저 즐겨찾기 저장
* @param {Set<string>} set
*/
function writeSet(set) {
function writeLocalSet(set) {
localStorage.setItem(STORAGE_KEY, JSON.stringify([...set]));
}
/**
* 즐겨찾기 초기화 및 기존 브라우저 데이터 이전
* @returns {Promise<void>}
*/
function init() {
if (initPromise) return initPromise;
initPromise = (async () => {
const localFavorites = readLocalSet();
if (!UaDeckApi.getToken()) {
favorites = localFavorites;
return;
}
try {
const data = localFavorites.size
? await UaDeckApi.syncFavorites([...localFavorites])
: await UaDeckApi.getFavorites();
favorites = new Set(data.favorites || []);
localStorage.removeItem(STORAGE_KEY);
} catch (err) {
favorites = localFavorites;
console.warn('[favorites] 계정 즐겨찾기 동기화 실패', err);
}
})();
return initPromise;
}
/**
* @param {string} shareCode
* @returns {boolean}
*/
function isFavorite(shareCode) {
if (!shareCode) return false;
return readSet().has(shareCode);
return Boolean(shareCode) && favorites.has(shareCode);
}
/**
* 즐겨찾기 상태 변경
* @param {string} shareCode
* @returns {boolean} 토글 후 즐겨찾기 여부
* @returns {Promise<boolean>} 변경 후 즐겨찾기 여부
*/
function toggle(shareCode) {
async function toggle(shareCode) {
if (!shareCode) return false;
const set = readSet();
if (set.has(shareCode)) {
set.delete(shareCode);
} else {
set.add(shareCode);
await init();
const next = !favorites.has(shareCode);
if (UaDeckApi.getToken()) {
if (next) {
await UaDeckApi.addFavorite(shareCode);
} else {
await UaDeckApi.removeFavorite(shareCode);
}
}
writeSet(set);
return set.has(shareCode);
if (next) favorites.add(shareCode);
else favorites.delete(shareCode);
if (!UaDeckApi.getToken()) writeLocalSet(favorites);
return next;
}
/**
* 삭제된 덱을 현재 화면 상태에서도 제거
* @param {string} shareCode
*/
function forget(shareCode) {
favorites.delete(shareCode);
if (!UaDeckApi.getToken()) writeLocalSet(favorites);
}
/**
* @returns {string[]}
*/
function getAll() {
return [...readSet()];
return [...favorites];
}
window.UaDeckFavorites = { isFavorite, toggle, getAll };
window.UaDeckFavorites = { init, isFavorite, toggle, forget, getAll };
})();