This commit is contained in:
2026-03-09 14:36:19 -04:00
parent f37fe0086f
commit 42ad779750
40 changed files with 1928 additions and 593 deletions

View File

@@ -4,6 +4,17 @@ function getToken() {
return localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token');
}
// SQLite datetime('now') returns "YYYY-MM-DD HH:MM:SS" with no timezone marker.
// Browsers parse bare strings like this as LOCAL time, but the value is actually UTC.
// Appending 'Z' forces correct UTC interpretation so local display is always right.
export function parseTS(ts) {
if (!ts) return new Date(NaN);
// Already has timezone info (contains T and Z/+ or ends in Z) — leave alone
if (/Z$|[+-]\d{2}:\d{2}$/.test(ts) || (ts.includes('T') && ts.includes('Z'))) return new Date(ts);
// Replace the space separator SQLite uses and append Z
return new Date(ts.replace(' ', 'T') + 'Z');
}
async function req(method, path, body, opts = {}) {
const token = getToken();
const headers = {};
@@ -45,11 +56,13 @@ export const api = {
searchUsers: (q) => req('GET', `/users/search?q=${encodeURIComponent(q)}`),
createUser: (body) => req('POST', '/users', body),
bulkUsers: (users) => req('POST', '/users/bulk', { users }),
updateName: (id, name) => req('PATCH', `/users/${id}/name`, { name }),
updateRole: (id, role) => req('PATCH', `/users/${id}/role`, { role }),
resetPassword: (id, password) => req('PATCH', `/users/${id}/reset-password`, { password }),
suspendUser: (id) => req('PATCH', `/users/${id}/suspend`),
activateUser: (id) => req('PATCH', `/users/${id}/activate`),
deleteUser: (id) => req('DELETE', `/users/${id}`),
checkDisplayName: (name) => req('GET', `/users/check-display-name?name=${encodeURIComponent(name)}`),
updateProfile: (body) => req('PATCH', '/users/me/profile', body), // body: { displayName, aboutMe, hideAdminTag }
uploadAvatar: (file) => {
const form = new FormData(); form.append('avatar', file);