refactor: standardize theme colors, update UI components, and refine auth redirection logic

This commit is contained in:
Firman Ramdhani
2026-07-31 11:49:09 +07:00
parent f91bbefddc
commit 2c8c9d22b4
10 changed files with 95 additions and 70 deletions
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { Drawer, Text, Stack, NavLink, ThemeIcon, ActionIcon, ScrollArea, Group } from '@repo/ui/components'; import { Drawer, Text, Stack, NavLink, ThemeIcon, ActionIcon, ScrollArea, Group } from '@repo/ui/components';
import { useAppEvent } from '@repo/core-events'; import { useAppEvent } from '@repo/core-events';
import { useTranslation } from '@repo/core-i18n'; import { useTranslation } from '@repo/core-i18n';
import { Bookmark, Trash2 } from 'lucide-react'; import { Bookmark, Trash } from 'lucide-react';
import { LAYOUT_EVENTS } from '../../../../../core/constants/events'; import { LAYOUT_EVENTS } from '../../../../../core/constants/events';
import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local'; import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@@ -85,11 +85,11 @@ export function BookmarkDrawer() {
<Group justify="flex-end" px="md" py="xs"> <Group justify="flex-end" px="md" py="xs">
<Text <Text
size="xs" size="xs"
c="red" c="var(--mantine-color-error-6)"
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '4px' }} style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '4px' }}
onClick={handleClearAll} onClick={handleClearAll}
> >
<Trash2 size={12} /> {t('bookmark:clearAll')} <Trash size={12} /> {t('bookmark:clearAll')}
</Text> </Text>
</Group> </Group>
<ScrollArea flex={1} type="scroll" px="xs" py="xs" pb={'xl'}> <ScrollArea flex={1} type="scroll" px="xs" py="xs" pb={'xl'}>
@@ -120,7 +120,7 @@ export function BookmarkDrawer() {
rightSection={ rightSection={
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
color="red" color="var(--mantine-color-error-6)"
size="sm" size="sm"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
@@ -128,7 +128,7 @@ export function BookmarkDrawer() {
}} }}
aria-label={t('bookmark:removeItem')} aria-label={t('bookmark:removeItem')}
> >
<Trash2 size={14} /> <Trash size={14} />
</ActionIcon> </ActionIcon>
} }
styles={{ styles={{
@@ -63,7 +63,7 @@ export default function HeaderLayout() {
{t('common:cancel')} {t('common:cancel')}
</Button> </Button>
<Button <Button
color="red" color="var(--mantine-color-error-6)"
size="xs" size="xs"
onClick={() => { onClick={() => {
modals.close('logout-confirmation'); modals.close('logout-confirmation');
@@ -237,7 +237,11 @@ export default function HeaderLayout() {
<Menu.Divider /> <Menu.Divider />
{/* Sign Out Section */} {/* Sign Out Section */}
<Menu.Item color="red" leftSection={<LogOut size={14} />} onClick={() => handleLogout()}> <Menu.Item
color="var(--mantine-color-error-6)"
leftSection={<LogOut size={14} />}
onClick={() => handleLogout()}
>
{t('common:signOut')} {t('common:signOut')}
</Menu.Item> </Menu.Item>
</Menu.Dropdown> </Menu.Dropdown>
@@ -13,7 +13,7 @@ import {
} from '@repo/ui/components'; } from '@repo/ui/components';
import { useAppEvent } from '@repo/core-events'; import { useAppEvent } from '@repo/core-events';
import { useTranslation } from '@repo/core-i18n'; import { useTranslation } from '@repo/core-i18n';
import { History, Clock, Trash2 } from 'lucide-react'; import { History, Clock, Trash } from 'lucide-react';
import { LAYOUT_EVENTS } from '../../../../../core/constants/events'; import { LAYOUT_EVENTS } from '../../../../../core/constants/events';
import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local'; import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@@ -129,7 +129,7 @@ export function HistoryDrawer() {
<Menu.Target> <Menu.Target>
<UnstyledButton> <UnstyledButton>
<Group gap={6} c="dimmed" style={{ transition: 'color 0.2s' }}> <Group gap={6} c="dimmed" style={{ transition: 'color 0.2s' }}>
<Trash2 size={14} /> <Trash size={14} />
<Text size="xs" fw={500} style={{ cursor: 'pointer' }}> <Text size="xs" fw={500} style={{ cursor: 'pointer' }}>
{t('history:clearRange')} {t('history:clearRange')}
</Text> </Text>
@@ -142,7 +142,11 @@ export function HistoryDrawer() {
<Menu.Item onClick={() => handleClearRange('lastHour')}>{t('history:ranges.lastHour')}</Menu.Item> <Menu.Item onClick={() => handleClearRange('lastHour')}>{t('history:ranges.lastHour')}</Menu.Item>
<Menu.Item onClick={() => handleClearRange('today')}>{t('history:ranges.today')}</Menu.Item> <Menu.Item onClick={() => handleClearRange('today')}>{t('history:ranges.today')}</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item color="red" leftSection={<Trash2 size={14} />} onClick={() => handleClearRange('allTime')}> <Menu.Item
color="var(--mantine-color-error-6)"
leftSection={<Trash size={14} />}
onClick={() => handleClearRange('allTime')}
>
{t('history:ranges.allTime')} {t('history:ranges.allTime')}
</Menu.Item> </Menu.Item>
</Menu.Dropdown> </Menu.Dropdown>
@@ -177,7 +181,7 @@ export function HistoryDrawer() {
rightSection={ rightSection={
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
color="red" color="var(--mantine-color-error-6)"
size="sm" size="sm"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
@@ -185,7 +189,7 @@ export function HistoryDrawer() {
}} }}
aria-label={t('history:removeItem')} aria-label={t('history:removeItem')}
> >
<Trash2 size={14} /> <Trash size={14} />
</ActionIcon> </ActionIcon>
} }
styles={{ styles={{
@@ -67,7 +67,14 @@ export function NotificationDropdown({ children }: NotificationDropdownProps) {
<Popover.Target> <Popover.Target>
{/* Trigger wrapped around the Bell icon */} {/* Trigger wrapped around the Bell icon */}
<Box onClick={() => setOpened((o) => !o)} style={{ cursor: 'pointer' }}> <Box onClick={() => setOpened((o) => !o)} style={{ cursor: 'pointer' }}>
<Indicator inline size={9} offset={4} color="red" withBorder disabled={unreadCount === 0}> <Indicator
inline
size={9}
offset={4}
color="var(--mantine-color-error-6)"
withBorder
disabled={unreadCount === 0}
>
{children} {children}
</Indicator> </Indicator>
</Box> </Box>
@@ -99,7 +99,7 @@ export default function SettingPage() {
<Button variant="default" onClick={handleCancelSwitch}> <Button variant="default" onClick={handleCancelSwitch}>
{t('setting:unsaved.cancel')} {t('setting:unsaved.cancel')}
</Button> </Button>
<Button color="red" onClick={handleDiscardChanges}> <Button color="var(--mantine-color-error-6)" onClick={handleDiscardChanges}>
{t('setting:unsaved.discard')} {t('setting:unsaved.discard')}
</Button> </Button>
</Group> </Group>
+14 -8
View File
@@ -25,15 +25,21 @@ export async function terminateAuthSession(options: TerminateOptions = {}) {
*/ */
let loginUrl = '/auth/login'; let loginUrl = '/auth/login';
if (preserveRedirect) { // Check if the current page is NOT the login page
const currentPath = window.location.pathname + window.location.search; const isNotLoginPage = !window.location.pathname.includes('/auth/login');
loginUrl += `?redirect=${encodeURIComponent(currentPath)}`;
}
/** if (isNotLoginPage) {
* Redirect without saving history to prevent infinite back-loops // Set redirect parameters only if requested AND the user is not currently on the login page
*/ if (preserveRedirect) {
window.location.replace(loginUrl); const currentPath = window.location.pathname + window.location.search;
loginUrl += `?redirect=${encodeURIComponent(currentPath)}`;
}
/**
* Redirect without saving history to prevent infinite back-loops
*/
window.location.replace(loginUrl);
}
} }
export async function initiateAuthSession(respLogin: any) { export async function initiateAuthSession(respLogin: any) {
@@ -3,7 +3,7 @@
"save": "Simpan", "save": "Simpan",
"cancel": "Batal", "cancel": "Batal",
"success": "Sukses", "success": "Sukses",
"error": "Galat", "error": "Error",
"settings": "Pengaturan", "settings": "Pengaturan",
"loading": "Memuat", "loading": "Memuat",
"delete": "Hapus", "delete": "Hapus",
@@ -144,7 +144,7 @@
}, },
"notifications": { "notifications": {
"successTitle": "Berhasil", "successTitle": "Berhasil",
"errorTitle": "Galat", "errorTitle": "Error",
"saveSuccess": "Data berhasil disimpan", "saveSuccess": "Data berhasil disimpan",
"deleteSuccess": "Data berhasil dihapus", "deleteSuccess": "Data berhasil dihapus",
"actionSuccess": "{{action}} berhasil diselesaikan", "actionSuccess": "{{action}} berhasil diselesaikan",
@@ -188,7 +188,7 @@ export function EnterpriseFormPageProvider<E extends BaseEntity = BaseEntity>(pr
notifications.show({ notifications.show({
title: t('common:notifications.errorTitle'), title: t('common:notifications.errorTitle'),
message: message ?? error?.message, message: message ?? error?.message,
color: 'red', color: 'var(--mantine-color-error-6)',
}); });
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -250,7 +250,7 @@ export function EnterpriseFormPageProvider<E extends BaseEntity = BaseEntity>(pr
notifications.show({ notifications.show({
title: t('common:notifications.errorTitle'), title: t('common:notifications.errorTitle'),
message: message ?? error?.message, message: message ?? error?.message,
color: 'red', color: 'var(--mantine-color-error-6)',
}); });
} finally { } finally {
setIsSaving(false); setIsSaving(false);
@@ -355,7 +355,7 @@ export function EnterpriseFormPageProvider<E extends BaseEntity = BaseEntity>(pr
{t('common:confirmDialog.leave.cancel')} {t('common:confirmDialog.leave.cancel')}
</Button> </Button>
<Button <Button
color="red" color="var(--mantine-color-error-6)"
size="xs" size="xs"
onClick={() => { onClick={() => {
actionTaken = true; actionTaken = true;
+7 -1
View File
@@ -35,11 +35,17 @@ export function ThemeProvider({ children, colorScheme = 'light', density = 'comp
black: '#1A1B1E', black: '#1A1B1E',
colors: { colors: {
brand: brandColors, brand: brandColors,
dark: darkColors,
error: errorColors, error: errorColors,
warning: warningColors, warning: warningColors,
success: successColors, success: successColors,
info: infoColors, info: infoColors,
dark: darkColors,
red: errorColors,
yellow: warningColors,
green: successColors,
blue: infoColors,
}, },
primaryColor: 'brand', primaryColor: 'brand',
fontFamily: typography.fontFamily, fontFamily: typography.fontFamily,
+40 -42
View File
@@ -12,57 +12,56 @@ export const brandColors: MantineColorsTuple = [
'#321a7a', // 800 '#321a7a', // 800
'#1b0f4a', // 900 '#1b0f4a', // 900
]; ];
export const errorColors: MantineColorsTuple = [ export const errorColors: MantineColorsTuple = [
'#fef2f2', '#fef3f2', // 0: 50
'#fee2e2', '#fee4e2', // 1: 100
'#fecaca', '#fecdca', // 2: 200
'#fca5a5', '#fda29b', // 3: 300
'#f87171', '#f97066', // 4: 400
'#ef4444', '#f04438', // 5: 500
'#dc2626', '#d92d20', // 6: 600 (Primary)
'#b91c1c', '#b42318', // 7: 700
'#991b1b', '#912018', // 8: 800
'#7f1d1d', '#7a271a', // 9: 900
]; ];
export const warningColors: MantineColorsTuple = [ export const warningColors: MantineColorsTuple = [
'#fffbeb', '#fffdf5', // 0: 50
'#fef3c7', '#fff9db', // 1: 100
'#fde68a', '#fff0b3', // 2: 200
'#fcd34d', '#ffe480', // 3: 300
'#fbbf24', '#ffd64d', // 4: 400
'#f59e0b', '#ffc71a', // 5: 500
'#d97706', '#ffb800', // 6: 600 (Primary)
'#b45309', '#d19200', // 7: 700
'#92400e', '#a67000', // 8: 800
'#78350f', '#7a5000', // 9: 900
]; ];
export const successColors: MantineColorsTuple = [ export const successColors: MantineColorsTuple = [
'#f0fdf4', '#f3fcf0', // 0: 50
'#dcfce7', '#e3f8dc', // 1: 100
'#bbf7d0', '#c6f0b7', // 2: 200
'#86efac', '#a0e589', // 3: 300
'#4ade80', '#7bd760', // 4: 400
'#22c55e', '#5ace3c', // 5: 500
'#16a34a', '#3ec300', // 6: 600 (Primary)
'#15803d', '#309b00', // 7: 700
'#166534', '#267b00', // 8: 800
'#14532d', '#206505', // 9: 900
]; ];
export const infoColors: MantineColorsTuple = [ export const infoColors: MantineColorsTuple = [
'#f0f9ff', '#eef7ff', // 0: 50
'#e0f2fe', '#d9efff', // 1: 100
'#bae6fd', '#b0ddff', // 2: 200
'#7dd3fc', '#82c9ff', // 3: 300
'#38bdf8', '#54b5ff', // 4: 400
'#0ea5e9', '#26a1ff', // 5: 500
'#0284c7', '#0094ff', // 6: 600 (Primary)
'#0369a1', '#0079d6', // 7: 700
'#075985', '#0060ad', // 8: 800
'#0c4a6e', '#004b87', // 9: 900
]; ];
export const grayColors: MantineColorsTuple = [ export const grayColors: MantineColorsTuple = [
@@ -104,4 +103,3 @@ export const darkColors: MantineColorsTuple = [
'#151828', // 8 Deep background (sidebar, nav) '#151828', // 8 Deep background (sidebar, nav)
'#0F1120', // 9 Deepest background '#0F1120', // 9 Deepest background
]; ];