User
Deploy theme persistence and navigation fix
e184579
Raw
History Blame Contribute Delete
22.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="firebase-app-compat.js"></script>
<script src="firebase-messaging-compat.js"></script>
<script src="firebase-config.js"></script>
<script src="firebase-messaging.js"></script>
<script>
(() => {
const storageKey = 'tts-theme';
let theme = 'light';
try {
const storedTheme = localStorage.getItem(storageKey);
theme = storedTheme === 'dark' || storedTheme === 'light' ? storedTheme : 'light';
localStorage.setItem(storageKey, theme);
} catch {
}
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.dataset.theme = theme;
})();
</script>
<script>
(() => {
const storageKey = 'tts-language';
let language = 'en';
try {
language = localStorage.getItem(storageKey) === 'my' ? 'my' : 'en';
} catch {
}
document.documentElement.lang = language;
})();
</script>
<script>
window.languagePreference = {
normalize(code) {
return code === 'my' ? 'my' : 'en';
},
apply(storageKey, code) {
const normalized = this.normalize(code);
document.documentElement.lang = normalized;
try {
localStorage.setItem(storageKey, normalized);
} catch {
}
return normalized;
},
init(storageKey) {
let code = 'en';
try {
code = this.normalize(localStorage.getItem(storageKey));
} catch {
}
return this.apply(storageKey, code);
},
toggle(storageKey) {
const next = document.documentElement.lang === 'my' ? 'en' : 'my';
return this.apply(storageKey, next);
},
set(storageKey, code) {
return this.apply(storageKey, code);
}
};
</script>
<script>
const brandDark = "#12312D";
const brandDarkAlt = "#115E59";
const brandPrimary = "#0F766E";
const brandPrimaryHover = "#115E59";
const brandAccent = "#B45309";
const brandLight = "#E6F4F1";
const brandMuted = "#64706D";
const semanticRedBg = "#FCE4E1";
const semanticRedText = "#B42318";
const semanticGreenBg = "#DFF6EE";
const semanticGreenText = "#0F766E";
const semanticNeutralBg = "#F1F5F4";
const semanticNeutralText = "#4F5D59";
const neutralBg = "#F7F8F7";
const neutralBorder = "#DDE5E2";
const neutralMuted = "#64706D";
const neutralDark = "#17211F";
const customGray = {
50: neutralBg,
100: "#EFF3F1",
200: neutralBorder,
300: "#B9C8C3",
400: "#8B9894",
500: neutralMuted,
600: "#4F5D59",
700: "#34413D",
800: "#222C29",
900: neutralDark,
950: "#101614"
};
tailwind.config = {
darkMode: 'class',
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
white: '#ffffff',
black: '#000000',
slate: customGray,
gray: customGray,
zinc: customGray,
neutral: customGray,
stone: customGray,
red: {
50: semanticRedBg,
100: semanticRedBg,
200: "#FECDD3",
300: "#FDA4AF",
400: "#FB7185",
500: semanticRedText,
600: semanticRedText,
700: semanticRedText,
800: semanticRedText,
900: semanticRedText
},
rose: {
50: semanticRedBg,
100: semanticRedBg,
200: "#FECDD3",
300: "#FDA4AF",
400: "#FB7185",
500: semanticRedText,
600: semanticRedText,
700: semanticRedText,
800: semanticRedText,
900: semanticRedText
},
green: {
50: semanticGreenBg,
100: semanticGreenBg,
200: "#A7F3D0",
300: "#6EE7B7",
400: "#34D399",
500: semanticGreenText,
600: semanticGreenText,
700: semanticGreenText,
800: semanticGreenText,
900: semanticGreenText
},
emerald: {
50: semanticGreenBg,
100: semanticGreenBg,
200: "#A7F3D0",
300: "#6EE7B7",
400: "#34D399",
500: semanticGreenText,
600: semanticGreenText,
700: semanticGreenText,
800: semanticGreenText,
900: semanticGreenText
},
teal: {
50: "#F0FDFA",
100: "#CCFBF1",
200: "#99F6E4",
300: "#5EEAD4",
400: "#2DD4BF",
500: "#14B8A6",
600: "#0D9488",
700: "#0F766E",
800: "#115E59",
900: "#134E4A"
},
cyan: {
50: "#ECFEFF",
100: "#CFFAFE",
200: "#A5F3FC",
300: "#67E8F9",
400: "#22D3EE",
500: "#06B6D4",
600: "#0891B2",
700: "#0E7490",
800: "#155E75",
900: "#164E63"
},
sky: {
50: "#F0F9FF",
100: "#E0F2FE",
200: "#BAE6FD",
300: "#7DD3FC",
400: "#38BDF8",
500: "#0EA5E9",
600: "#0284C7",
700: "#0369A1",
800: "#075985",
900: "#0C4A6E"
},
blue: {
50: brandLight,
100: "#CFE8E3",
200: "#A7D8CF",
300: "#76C3B8",
400: "#3AA89A",
500: "#16877E",
600: brandPrimary,
700: brandPrimaryHover,
800: brandDarkAlt,
900: brandDark
},
amber: {
50: "#FEF3C7",
100: "#FDE68A",
200: "#FCD34D",
300: "#FBBF24",
400: "#F59E0B",
500: "#B45309",
600: "#92400E",
700: "#78350F",
800: "#78350F",
900: "#78350F"
},
yellow: {
50: "#FEFCE8",
100: "#FEF3C7",
200: "#FDE68A",
300: "#FCD34D",
400: "#F59E0B",
500: "#B45309",
600: "#92400E",
700: "#78350F",
800: "#78350F",
900: "#78350F"
},
violet: {
50: brandLight,
100: "#CFE8E3",
200: "#A7D8CF",
300: "#76C3B8",
400: "#3AA89A",
500: "#16877E",
600: brandPrimary,
700: brandPrimaryHover,
800: brandDark,
900: brandDark,
950: brandDark
},
purple: {
50: brandLight,
100: "#CFE8E3",
200: "#A7D8CF",
300: "#76C3B8",
400: "#3AA89A",
500: "#16877E",
600: brandPrimary,
700: brandPrimaryHover,
800: brandDark,
900: brandDark,
950: brandDark
},
indigo: {
50: brandLight,
100: "#CFE8E3",
200: "#A7D8CF",
300: "#76C3B8",
400: "#3AA89A",
500: "#16877E",
600: brandPrimary,
700: brandPrimaryHover,
800: brandDark,
900: brandDark,
950: brandDark
},
chart: {
purple: brandPrimary,
teal: "#14B8A6",
green: "#0F766E",
neutral: neutralBorder
},
background: neutralBg,
foreground: neutralDark,
card: "#FFFFFF",
"card-foreground": neutralDark,
popover: "#FFFFFF",
"popover-foreground": neutralDark,
primary: brandPrimary,
"primary-foreground": "#FFFFFF",
secondary: brandLight,
"secondary-foreground": neutralDark,
muted: "#F3F4F6",
"muted-foreground": neutralMuted,
accent: brandAccent,
"accent-foreground": "#FFFFFF",
border: neutralBorder,
},
borderRadius: {
none: "0",
sm: "0.375rem",
DEFAULT: "0.5rem",
md: "0.5rem",
lg: "0.5rem",
xl: "0.5rem",
"2xl": "0.5rem",
"3xl": "0.5rem",
full: "9999px"
}
}
}
</script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="TaskTrackingSystem.WebApp.styles.css" />
<link rel="stylesheet" href="app.css" />
<script src="theme-toggle.js?v=20260720-3" defer></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/gantt/modules/gantt.js"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
<script>
window.renderPieChart = function (containerId, data) {
const container = document.getElementById(containerId);
if (!container) return;
Highcharts.chart(containerId, {
chart: {
type: 'pie',
backgroundColor: 'transparent',
height: 260,
margin: [0, 0, 0, 0],
spacing: [0, 0, 0, 0]
},
title: {
text: null
},
tooltip: {
backgroundColor: 'rgba(255, 255, 255, 0.97)',
borderWidth: 1,
borderColor: '#dde5e2',
borderRadius: 8,
shadow: true,
style: {
fontFamily: 'Inter, sans-serif',
fontSize: '13px',
color: '#17211f'
},
pointFormat: '<b>{point.name}</b>: <b>{point.y}</b> tasks ({point.percentage:.1f}%)'
},
plotOptions: {
pie: {
borderWidth: 0,
borderColor: 'transparent',
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
distance: -30,
format: '{point.percentage:.0f}%',
style: {
color: 'white',
textOutline: 'none',
fontFamily: 'Inter, sans-serif',
fontSize: '12px',
fontWeight: 'bold'
},
filter: {
property: 'percentage',
operator: '>',
value: 5
}
},
showInLegend: false
}
},
series: [{
name: 'Tasks',
colorByPoint: true,
data: data
}],
credits: {
enabled: false
}
});
};
window.renderGanttChart = function (containerId, data, options) {
options = options || {};
const container = document.getElementById(containerId);
if (!container) return;
const minWidth = options.minWidth || 1200;
const chartHeight = Math.max(options.height || 420, 120 + (data.length * 44));
Highcharts.ganttChart(containerId, {
chart: {
backgroundColor: 'transparent',
height: chartHeight,
style: {
fontFamily: 'Inter, sans-serif'
},
scrollablePlotArea: {
minWidth: minWidth,
scrollPositionX: 0
}
},
title: {
text: null
},
credits: {
enabled: false
},
navigator: {
enabled: false
},
scrollbar: {
enabled: true
},
rangeSelector: {
enabled: false
},
legend: {
enabled: false
},
tooltip: {
backgroundColor: 'rgba(255, 255, 255, 0.97)',
borderWidth: 1,
borderColor: '#dde5e2',
borderRadius: 8,
shadow: true,
style: {
fontSize: '13px',
color: '#17211f'
},
pointFormatter: function() {
const start = Highcharts.dateFormat('%b %e, %Y', this.start);
const end = Highcharts.dateFormat('%b %e, %Y', this.end);
const progress = this.completed && this.completed.amount !== undefined
? Math.round(this.completed.amount * 100) + '%'
: '0%';
return `<b>${this.name}</b><br/>Start: ${start}<br/>End: ${end}<br/>Progress: ${progress}`;
}
},
xAxis: [{
type: 'datetime',
min: options.xAxisMin,
max: options.xAxisMax,
gridLineWidth: 1,
gridLineColor: '#eff3f1',
lineColor: '#dde5e2',
tickColor: '#dde5e2',
labels: {
format: '{value:%b %Y}',
style: {
color: '#64706d',
fontSize: '11px'
}
},
currentDateIndicator: {
color: '#0f766e',
dashStyle: 'ShortDash',
width: 2,
label: {
format: 'Today',
style: {
color: '#0f766e',
fontWeight: 'bold'
}
}
}
}],
yAxis: {
gridLineWidth: 1,
gridLineColor: '#eff3f1',
labels: {
style: {
color: '#34413d',
fontSize: '12px',
fontWeight: '600'
}
}
},
plotOptions: {
gantt: {
borderWidth: 0,
borderColor: 'transparent',
dataLabels: {
enabled: true,
formatter: function() {
if (this.point.completed && this.point.completed.amount !== undefined) {
return Math.round(this.point.completed.amount * 100) + '%';
}
return '0%';
},
style: {
cursor: 'default',
pointerEvents: 'none',
textOutline: 'none',
color: '#ffffff',
fontSize: '11px',
fontWeight: 'bold'
}
}
}
},
series: [{
name: 'Projects',
borderWidth: 0,
borderColor: 'transparent',
data: data.map(item => ({
id: item.id,
name: item.name,
start: item.start,
end: item.end,
completed: {
amount: item.completed,
fill: item.color
},
color: '#dde5e2'
}))
}]
});
};
function initIcons() {
if (window.lucide) {
window.lucide.createIcons();
}
if (typeof initFlatpickr === 'function') {
initFlatpickr();
}
}
function downloadFile(filename, contentType, content) {
const file = new Blob([content], {type: contentType});
const exportUrl = URL.createObjectURL(file);
const a = document.createElement("a");
a.href = exportUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(exportUrl);
}
function downloadFileFromBase64(filename, contentType, base64Content) {
const byteCharacters = atob(base64Content);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const file = new Blob([byteArray], {type: contentType});
const exportUrl = URL.createObjectURL(file);
const a = document.createElement("a");
a.href = exportUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(exportUrl);
}
function initFlatpickr() {
if (window.flatpickr) {
flatpickr("input[type='date']", {
altInput: true,
altFormat: "d-m-Y",
dateFormat: "Y-m-d",
allowInput: true
});
}
}
function refreshClientEnhancements() {
if (window.updateTaskifyThemeControls) {
window.updateTaskifyThemeControls();
}
initIcons();
initFlatpickr();
}
// Run once loaded
window.addEventListener('DOMContentLoaded', () => {
refreshClientEnhancements();
});
// Blazor enhanced page updates trigger
Blazor.addEventListener('enhancedload', () => {
refreshClientEnhancements();
});
</script>
</body>
</html>