Initial commit: timeline-elements-demo app
Interactive Angular 19 demo for @sda/timeline-elements-ui with vertical/horizontal timelines, Gantt charts, activity feeds, audit trails, changelogs, and dark mode toggle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
.angular/
|
||||||
|
*.tgz
|
||||||
62
angular.json
Normal file
62
angular.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"timeline-elements-demo": {
|
||||||
|
"projectType": "application",
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:application",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/timeline-elements-demo",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"browser": "src/main.ts",
|
||||||
|
"polyfills": ["zone.js"],
|
||||||
|
"tsConfig": "tsconfig.json",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "src/assets"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"styles": ["src/styles.scss"]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "500kB",
|
||||||
|
"maximumError": "1MB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "timeline-elements-demo:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "timeline-elements-demo:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13908
package-lock.json
generated
Normal file
13908
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "timeline-elements-demo",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^19.1.0",
|
||||||
|
"@angular/common": "^19.1.0",
|
||||||
|
"@angular/compiler": "^19.1.0",
|
||||||
|
"@angular/core": "^19.1.0",
|
||||||
|
"@angular/forms": "^19.1.0",
|
||||||
|
"@angular/platform-browser": "^19.1.0",
|
||||||
|
"@angular/platform-browser-dynamic": "^19.1.0",
|
||||||
|
"@angular/router": "^19.1.0",
|
||||||
|
"@sda/base-ui": "file:../../sda-frontend/libs/base-ui/dist",
|
||||||
|
"@sda/timeline-elements-ui": "file:../timeline-elements-ui/dist",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"zone.js": "~0.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^19.1.0",
|
||||||
|
"@angular/cli": "^19.1.0",
|
||||||
|
"@angular/compiler-cli": "^19.1.0",
|
||||||
|
"typescript": "~5.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
157
src/app/app.component.html
Normal file
157
src/app/app.component.html
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<div class="app">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="app__header">
|
||||||
|
<h1 class="app__title">Timeline Elements UI Demo</h1>
|
||||||
|
<div class="app__controls">
|
||||||
|
<button class="app__toggle" (click)="toggleDarkMode()">
|
||||||
|
{{ isDarkMode() ? '🌙' : '☀️' }} {{ isDarkMode() ? 'Dark' : 'Light' }} Mode
|
||||||
|
</button>
|
||||||
|
<button class="app__toggle" (click)="toggleTheme()">
|
||||||
|
Theme: {{ theme() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<nav class="app__sidebar">
|
||||||
|
@for (section of sections; track section.id) {
|
||||||
|
<button
|
||||||
|
class="app__nav-item"
|
||||||
|
[class.app__nav-item--active]="activeSection() === section.id"
|
||||||
|
(click)="setSection(section.id)"
|
||||||
|
>
|
||||||
|
{{ section.label }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="app__main">
|
||||||
|
@switch (activeSection()) {
|
||||||
|
@case ('timeline') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Vertical Timeline</h2>
|
||||||
|
<p>Tenancy history with markers, connectors, and date grouping</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<h3>Default Layout (Left-aligned)</h3>
|
||||||
|
<tl-timeline
|
||||||
|
[items]="tenancyHistory"
|
||||||
|
layout="left"
|
||||||
|
[groupByDate]="true"
|
||||||
|
[animate]="true"
|
||||||
|
(itemClick)="onItemClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<h3>Alternating Layout</h3>
|
||||||
|
<tl-timeline
|
||||||
|
[items]="tenancyHistory"
|
||||||
|
layout="alternating"
|
||||||
|
markerSize="lg"
|
||||||
|
[sortDirection]="'asc'"
|
||||||
|
(itemClick)="onItemClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@case ('horizontal') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Horizontal Timeline</h2>
|
||||||
|
<p>Scrollable horizontal timeline with navigation arrows</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<tl-horizontal-timeline
|
||||||
|
[items]="tenancyHistory"
|
||||||
|
[showNavArrows]="true"
|
||||||
|
[scrollable]="true"
|
||||||
|
(itemClick)="onItemClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@case ('activity') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Activity Feed</h2>
|
||||||
|
<p>Social-style activity stream with avatars and load-more</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<tl-activity-feed
|
||||||
|
[items]="recentActivity"
|
||||||
|
[pageSize]="5"
|
||||||
|
[showLoadMore]="true"
|
||||||
|
[showUnreadIndicator]="true"
|
||||||
|
[groupByDate]="true"
|
||||||
|
(activityClick)="onActivityClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@case ('audit') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Audit Trail</h2>
|
||||||
|
<p>Comprehensive audit log with search, filter, and expandable change diffs</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<tl-audit-trail
|
||||||
|
[entries]="auditLog"
|
||||||
|
[pageSize]="10"
|
||||||
|
[searchable]="true"
|
||||||
|
[filterable]="true"
|
||||||
|
[expandable]="true"
|
||||||
|
[showSeverityIcons]="true"
|
||||||
|
(entryClick)="onAuditEntryClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@case ('changelog') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Changelog</h2>
|
||||||
|
<p>Version-based changelog with categorized entries and breaking changes</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<tl-changelog
|
||||||
|
[versions]="changelog"
|
||||||
|
[collapsible]="true"
|
||||||
|
[expandLatest]="true"
|
||||||
|
[showCategoryBadges]="true"
|
||||||
|
[showBreakingBadge]="true"
|
||||||
|
(versionClick)="onVersionClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@case ('gantt') {
|
||||||
|
<div class="app__section">
|
||||||
|
<h2>Gantt Chart</h2>
|
||||||
|
<p>Interactive Gantt chart with zoom, progress bars, and today line</p>
|
||||||
|
|
||||||
|
<div class="app__demo">
|
||||||
|
<tl-gantt-chart
|
||||||
|
[tasks]="renovationTasks"
|
||||||
|
[dependencies]="renovationDependencies"
|
||||||
|
zoomLevel="week"
|
||||||
|
[rowHeight]="40"
|
||||||
|
[showDependencies]="false"
|
||||||
|
[showProgress]="true"
|
||||||
|
[showTodayLine]="true"
|
||||||
|
(taskClick)="onTaskClick($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="app__footer">
|
||||||
|
<p>Timeline Elements UI • Built with @sda/base-ui • Angular 19</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
115
src/app/app.component.scss
Normal file
115
src/app/app.component.scss
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
.app {
|
||||||
|
display: grid;
|
||||||
|
grid-template:
|
||||||
|
'header header' auto
|
||||||
|
'sidebar main' 1fr
|
||||||
|
'footer footer' auto
|
||||||
|
/ 200px 1fr;
|
||||||
|
min-height: 100vh;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
grid-area: header;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sidebar {
|
||||||
|
grid-area: sidebar;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border-right: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
transition: all 150ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-bg-hover);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: var(--color-primary-50);
|
||||||
|
color: var(--color-primary-600);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main {
|
||||||
|
grid-area: main;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
> h2 {
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
margin: 0 0 2rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__demo {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
grid-area: footer;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/app/app.component.ts
Normal file
90
src/app/app.component.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {
|
||||||
|
TlTimelineComponent,
|
||||||
|
TlHorizontalTimelineComponent,
|
||||||
|
TlActivityFeedComponent,
|
||||||
|
TlAuditTrailComponent,
|
||||||
|
TlChangelogComponent,
|
||||||
|
TlGanttChartComponent,
|
||||||
|
} from '@sda/timeline-elements-ui';
|
||||||
|
import { TENANCY_HISTORY } from './mock-data/timeline-data';
|
||||||
|
import { RECENT_ACTIVITY } from './mock-data/activity-data';
|
||||||
|
import { AUDIT_LOG } from './mock-data/audit-data';
|
||||||
|
import { CHANGELOG } from './mock-data/changelog-data';
|
||||||
|
import { FACILITY_RENOVATION, RENOVATION_DEPENDENCIES } from './mock-data/gantt-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
TlTimelineComponent,
|
||||||
|
TlHorizontalTimelineComponent,
|
||||||
|
TlActivityFeedComponent,
|
||||||
|
TlAuditTrailComponent,
|
||||||
|
TlChangelogComponent,
|
||||||
|
TlGanttChartComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrl: './app.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
readonly activeSection = signal<string>('timeline');
|
||||||
|
readonly isDarkMode = signal(false);
|
||||||
|
readonly theme = signal<'apple' | 'mist'>('apple');
|
||||||
|
|
||||||
|
// Data
|
||||||
|
readonly tenancyHistory = TENANCY_HISTORY;
|
||||||
|
readonly recentActivity = RECENT_ACTIVITY;
|
||||||
|
readonly auditLog = AUDIT_LOG;
|
||||||
|
readonly changelog = CHANGELOG;
|
||||||
|
readonly renovationTasks = FACILITY_RENOVATION;
|
||||||
|
readonly renovationDependencies = RENOVATION_DEPENDENCIES;
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
readonly sections = [
|
||||||
|
{ id: 'timeline', label: 'Timeline' },
|
||||||
|
{ id: 'horizontal', label: 'Horizontal Timeline' },
|
||||||
|
{ id: 'activity', label: 'Activity Feed' },
|
||||||
|
{ id: 'audit', label: 'Audit Trail' },
|
||||||
|
{ id: 'changelog', label: 'Changelog' },
|
||||||
|
{ id: 'gantt', label: 'Gantt Chart' },
|
||||||
|
];
|
||||||
|
|
||||||
|
setSection(id: string): void {
|
||||||
|
this.activeSection.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDarkMode(): void {
|
||||||
|
this.isDarkMode.update(v => !v);
|
||||||
|
document.body.setAttribute('data-mode', this.isDarkMode() ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTheme(): void {
|
||||||
|
this.theme.update(t => t === 'apple' ? 'mist' : 'apple');
|
||||||
|
document.body.setAttribute('data-theme', this.theme());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
onItemClick(event: any): void {
|
||||||
|
console.log('Item clicked:', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onActivityClick(event: any): void {
|
||||||
|
console.log('Activity clicked:', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAuditEntryClick(event: any): void {
|
||||||
|
console.log('Audit entry clicked:', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onVersionClick(event: any): void {
|
||||||
|
console.log('Version clicked:', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTaskClick(event: any): void {
|
||||||
|
console.log('Task clicked:', event);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/app/app.config.ts
Normal file
16
src/app/app.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { provideTimelineConfig } from '@sda/timeline-elements-ui';
|
||||||
|
import { routes } from './app.routes';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
|
provideRouter(routes),
|
||||||
|
provideTimelineConfig({
|
||||||
|
locale: 'en-US',
|
||||||
|
relativeTime: true,
|
||||||
|
animate: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
3
src/app/app.routes.ts
Normal file
3
src/app/app.routes.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
export const routes: Routes = [];
|
||||||
84
src/app/mock-data/activity-data.ts
Normal file
84
src/app/mock-data/activity-data.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import type { TlActivityItem } from '@sda/timeline-elements-ui';
|
||||||
|
|
||||||
|
export const RECENT_ACTIVITY: TlActivityItem[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
actor: { id: '1', name: 'Sarah Johnson', avatar: '', initials: 'SJ' },
|
||||||
|
action: 'created',
|
||||||
|
target: 'Incident Report #2453',
|
||||||
|
description: 'Fall in dining area - resident Mrs. Anderson',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 15),
|
||||||
|
type: 'create',
|
||||||
|
unread: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
actor: { id: '2', name: 'Michael Chen', avatar: '', initials: 'MC' },
|
||||||
|
action: 'updated',
|
||||||
|
target: 'Care Plan',
|
||||||
|
description: 'Revised medication schedule for Mr. Roberts',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 45),
|
||||||
|
type: 'update',
|
||||||
|
unread: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
actor: { id: '3', name: 'Emily Davis', avatar: '', initials: 'ED' },
|
||||||
|
action: 'commented on',
|
||||||
|
target: 'Incident Report #2453',
|
||||||
|
description: 'Added witness statement and photos',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 120),
|
||||||
|
type: 'comment',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
actor: { id: '4', name: 'System', avatar: '', initials: 'SY' },
|
||||||
|
action: 'assigned',
|
||||||
|
target: 'Maintenance Task #891',
|
||||||
|
description: 'Broken window in Room 12 → Facilities Team',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 240),
|
||||||
|
type: 'assign',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
actor: { id: '1', name: 'Sarah Johnson', avatar: '', initials: 'SJ' },
|
||||||
|
action: 'uploaded',
|
||||||
|
target: '3 documents',
|
||||||
|
description: 'Medical reports for quarterly review',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 360),
|
||||||
|
type: 'upload',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
actor: { id: '5', name: 'David Martinez', avatar: '', initials: 'DM' },
|
||||||
|
action: 'completed',
|
||||||
|
target: 'Fire Safety Drill',
|
||||||
|
description: 'All residents and staff participated successfully',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 60 * 24),
|
||||||
|
type: 'status',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
actor: { id: '3', name: 'Emily Davis', avatar: '', initials: 'ED' },
|
||||||
|
action: 'shared',
|
||||||
|
target: 'Weekly Schedule',
|
||||||
|
description: 'Activity calendar shared with family members',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 60 * 36),
|
||||||
|
type: 'share',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
actor: { id: '4', name: 'System', avatar: '', initials: 'SY' },
|
||||||
|
action: 'sent',
|
||||||
|
target: 'Medication Reminder',
|
||||||
|
description: 'Automated reminders sent to nursing staff',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 60 * 48),
|
||||||
|
type: 'system',
|
||||||
|
unread: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
98
src/app/mock-data/audit-data.ts
Normal file
98
src/app/mock-data/audit-data.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import type { TlAuditEntry } from '@sda/timeline-elements-ui';
|
||||||
|
|
||||||
|
export const AUDIT_LOG: TlAuditEntry[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
action: 'User Login',
|
||||||
|
category: 'Authentication',
|
||||||
|
user: 'sarah.johnson@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 10),
|
||||||
|
severity: 'info',
|
||||||
|
description: 'Successful login via SSO',
|
||||||
|
ipAddress: '192.168.1.45',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
action: 'Care Plan Modified',
|
||||||
|
category: 'Clinical',
|
||||||
|
user: 'michael.chen@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 30),
|
||||||
|
severity: 'warning',
|
||||||
|
description: 'Medication dosage updated for resident #1234',
|
||||||
|
resource: 'CarePlan:1234',
|
||||||
|
changes: [
|
||||||
|
{ field: 'medication.dosage', oldValue: '10mg', newValue: '15mg' },
|
||||||
|
{ field: 'medication.frequency', oldValue: 'Once daily', newValue: 'Twice daily' },
|
||||||
|
{ field: 'lastReviewedBy', oldValue: 'Dr. Smith', newValue: 'Dr. Johnson' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
action: 'Document Deleted',
|
||||||
|
category: 'Data',
|
||||||
|
user: 'emily.davis@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 75),
|
||||||
|
severity: 'error',
|
||||||
|
description: 'Incident report permanently deleted',
|
||||||
|
resource: 'IncidentReport:2451',
|
||||||
|
ipAddress: '192.168.1.78',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
action: 'Permission Changed',
|
||||||
|
category: 'Security',
|
||||||
|
user: 'admin@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 120),
|
||||||
|
severity: 'critical',
|
||||||
|
description: 'User role elevated to Administrator',
|
||||||
|
resource: 'User:8821',
|
||||||
|
changes: [
|
||||||
|
{ field: 'role', oldValue: 'Staff', newValue: 'Administrator' },
|
||||||
|
{ field: 'permissions', oldValue: 'read,write', newValue: 'read,write,admin,delete' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
action: 'Database Query',
|
||||||
|
category: 'System',
|
||||||
|
user: 'system@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 180),
|
||||||
|
severity: 'debug',
|
||||||
|
description: 'Automated backup completed successfully',
|
||||||
|
ipAddress: '10.0.0.5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
action: 'Export Data',
|
||||||
|
category: 'Data',
|
||||||
|
user: 'sarah.johnson@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 240),
|
||||||
|
severity: 'info',
|
||||||
|
description: 'Monthly report exported to CSV',
|
||||||
|
resource: 'Report:monthly-2024-03',
|
||||||
|
ipAddress: '192.168.1.45',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
action: 'Failed Login Attempt',
|
||||||
|
category: 'Authentication',
|
||||||
|
user: 'unknown@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 300),
|
||||||
|
severity: 'warning',
|
||||||
|
description: 'Invalid credentials entered 3 times',
|
||||||
|
ipAddress: '203.45.67.89',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
action: 'Configuration Changed',
|
||||||
|
category: 'System',
|
||||||
|
user: 'admin@example.com',
|
||||||
|
date: new Date(Date.now() - 1000 * 60 * 400),
|
||||||
|
severity: 'warning',
|
||||||
|
description: 'Email notification settings updated',
|
||||||
|
changes: [
|
||||||
|
{ field: 'notifications.email.enabled', oldValue: 'false', newValue: 'true' },
|
||||||
|
{ field: 'notifications.email.frequency', oldValue: null, newValue: 'immediate' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
86
src/app/mock-data/changelog-data.ts
Normal file
86
src/app/mock-data/changelog-data.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import type { TlChangelogVersion } from '@sda/timeline-elements-ui';
|
||||||
|
|
||||||
|
export const CHANGELOG: TlChangelogVersion[] = [
|
||||||
|
{
|
||||||
|
version: '2.1.0',
|
||||||
|
date: new Date('2024-03-15'),
|
||||||
|
title: 'Care Planning Enhancements',
|
||||||
|
description: 'Major updates to care plan workflow and medication tracking',
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
description: 'New medication interaction warnings in care plans',
|
||||||
|
category: 'added',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Real-time collaboration for care plan editing',
|
||||||
|
category: 'added',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Improved incident report workflow with better categorization',
|
||||||
|
category: 'changed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Fixed issue with care plan PDF exports missing signatures',
|
||||||
|
category: 'fixed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Enhanced medication schedule conflict detection',
|
||||||
|
category: 'security',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '2.0.0',
|
||||||
|
date: new Date('2024-02-01'),
|
||||||
|
title: 'Major Platform Upgrade',
|
||||||
|
description: 'Complete redesign with improved performance and accessibility',
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
description: 'Completely redesigned user interface with improved accessibility',
|
||||||
|
category: 'changed',
|
||||||
|
breaking: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'New dashboard with customizable widgets',
|
||||||
|
category: 'added',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Mobile app for iOS and Android',
|
||||||
|
category: 'added',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Legacy API endpoints deprecated (v1.x)',
|
||||||
|
category: 'deprecated',
|
||||||
|
reference: 'https://docs.example.com/api/migration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Removed Flash-based document viewer',
|
||||||
|
category: 'removed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Fixed critical security vulnerability in authentication',
|
||||||
|
category: 'security',
|
||||||
|
breaking: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '1.9.2',
|
||||||
|
date: new Date('2024-01-10'),
|
||||||
|
description: 'Bug fixes and performance improvements',
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
description: 'Fixed date picker not working in Firefox',
|
||||||
|
category: 'fixed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Improved page load times by 40%',
|
||||||
|
category: 'changed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Fixed memory leak in real-time notifications',
|
||||||
|
category: 'fixed',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
112
src/app/mock-data/gantt-data.ts
Normal file
112
src/app/mock-data/gantt-data.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import type { TlGanttTask, TlGanttDependency } from '@sda/timeline-elements-ui';
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const addDays = (date: Date, days: number) => new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
export const FACILITY_RENOVATION: TlGanttTask[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'Planning & Design',
|
||||||
|
startDate: addDays(today, -14),
|
||||||
|
endDate: addDays(today, -7),
|
||||||
|
progress: 100,
|
||||||
|
status: 'completed',
|
||||||
|
assignee: 'Architecture Team',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: 'Permits & Approvals',
|
||||||
|
startDate: addDays(today, -10),
|
||||||
|
endDate: addDays(today, -2),
|
||||||
|
progress: 100,
|
||||||
|
status: 'completed',
|
||||||
|
assignee: 'Legal Team',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
name: 'Common Area Renovation',
|
||||||
|
startDate: addDays(today, -1),
|
||||||
|
endDate: addDays(today, 20),
|
||||||
|
progress: 15,
|
||||||
|
status: 'in-progress',
|
||||||
|
group: 'Phase 1',
|
||||||
|
color: '#3b82f6',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
name: 'Dining Hall Upgrade',
|
||||||
|
startDate: addDays(today, 5),
|
||||||
|
endDate: addDays(today, 18),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
parentId: '3',
|
||||||
|
assignee: 'Construction Team A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
name: 'Lounge Area Remodeling',
|
||||||
|
startDate: addDays(today, 10),
|
||||||
|
endDate: addDays(today, 20),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
parentId: '3',
|
||||||
|
assignee: 'Construction Team B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
name: 'Room Renovations',
|
||||||
|
startDate: addDays(today, 21),
|
||||||
|
endDate: addDays(today, 50),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
group: 'Phase 2',
|
||||||
|
color: '#10b981',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
name: 'Rooms 1-10',
|
||||||
|
startDate: addDays(today, 21),
|
||||||
|
endDate: addDays(today, 35),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
parentId: '6',
|
||||||
|
assignee: 'Construction Team A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
name: 'Rooms 11-20',
|
||||||
|
startDate: addDays(today, 36),
|
||||||
|
endDate: addDays(today, 50),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
parentId: '6',
|
||||||
|
assignee: 'Construction Team B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
name: 'Final Inspection',
|
||||||
|
startDate: addDays(today, 51),
|
||||||
|
endDate: addDays(today, 55),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
assignee: 'Inspections Dept',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
name: 'Furniture Delivery',
|
||||||
|
startDate: addDays(today, 56),
|
||||||
|
endDate: addDays(today, 60),
|
||||||
|
progress: 0,
|
||||||
|
status: 'not-started',
|
||||||
|
assignee: 'Logistics Team',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const RENOVATION_DEPENDENCIES: TlGanttDependency[] = [
|
||||||
|
{ fromTaskId: '1', toTaskId: '2' },
|
||||||
|
{ fromTaskId: '2', toTaskId: '3' },
|
||||||
|
{ fromTaskId: '3', toTaskId: '6' },
|
||||||
|
{ fromTaskId: '7', toTaskId: '8' },
|
||||||
|
{ fromTaskId: '6', toTaskId: '9' },
|
||||||
|
{ fromTaskId: '9', toTaskId: '10' },
|
||||||
|
];
|
||||||
60
src/app/mock-data/timeline-data.ts
Normal file
60
src/app/mock-data/timeline-data.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type { TlTimelineItem } from '@sda/timeline-elements-ui';
|
||||||
|
|
||||||
|
export const TENANCY_HISTORY: TlTimelineItem[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: 'Tenancy Agreement Signed',
|
||||||
|
description: 'John Doe signed 12-month tenancy agreement for Unit 24B',
|
||||||
|
date: new Date('2024-01-15'),
|
||||||
|
icon: 'file-text',
|
||||||
|
status: 'completed',
|
||||||
|
category: 'Legal',
|
||||||
|
tags: ['contract', 'tenancy'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: 'Move-In Inspection',
|
||||||
|
description: 'Property condition documented with photos',
|
||||||
|
date: new Date('2024-01-20'),
|
||||||
|
icon: 'clipboard',
|
||||||
|
status: 'completed',
|
||||||
|
category: 'Inspection',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: 'First Rent Payment',
|
||||||
|
description: 'Rent payment of $1,200 received',
|
||||||
|
date: new Date('2024-02-01'),
|
||||||
|
icon: 'dollar-sign',
|
||||||
|
status: 'completed',
|
||||||
|
category: 'Financial',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
title: 'Maintenance Request',
|
||||||
|
description: 'Tenant reported leaking faucet in bathroom',
|
||||||
|
date: new Date('2024-03-10'),
|
||||||
|
icon: 'tool',
|
||||||
|
status: 'completed',
|
||||||
|
category: 'Maintenance',
|
||||||
|
tags: ['plumbing', 'urgent'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
title: 'Quarterly Inspection',
|
||||||
|
description: 'Routine property inspection scheduled',
|
||||||
|
date: new Date('2024-04-15'),
|
||||||
|
icon: 'eye',
|
||||||
|
status: 'active',
|
||||||
|
category: 'Inspection',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
title: 'Lease Renewal Reminder',
|
||||||
|
description: 'Automated reminder sent to tenant',
|
||||||
|
date: new Date('2024-12-15'),
|
||||||
|
icon: 'bell',
|
||||||
|
status: 'pending',
|
||||||
|
category: 'Administrative',
|
||||||
|
},
|
||||||
|
];
|
||||||
13
src/index.html
Normal file
13
src/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Timeline Elements UI Demo</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body data-mode="light" data-theme="apple">
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
src/main.ts
Normal file
6
src/main.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
|
import { appConfig } from './app/app.config';
|
||||||
|
import { AppComponent } from './app/app.component';
|
||||||
|
|
||||||
|
bootstrapApplication(AppComponent, appConfig)
|
||||||
|
.catch((err) => console.error(err));
|
||||||
69
src/styles.scss
Normal file
69
src/styles.scss
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
:root {
|
||||||
|
// Base colors
|
||||||
|
--color-bg-primary: #ffffff;
|
||||||
|
--color-bg-secondary: #f9fafb;
|
||||||
|
--color-bg-hover: #f3f4f6;
|
||||||
|
--color-border-primary: #e5e7eb;
|
||||||
|
--color-border-hover: #d1d5db;
|
||||||
|
--color-text-primary: #111827;
|
||||||
|
--color-text-secondary: #6b7280;
|
||||||
|
--color-text-muted: #9ca3af;
|
||||||
|
--color-text-inverse: #ffffff;
|
||||||
|
|
||||||
|
// Primary colors
|
||||||
|
--color-primary-50: #eff6ff;
|
||||||
|
--color-primary-500: #3b82f6;
|
||||||
|
--color-primary-600: #2563eb;
|
||||||
|
--color-primary-900: #1e3a5f;
|
||||||
|
|
||||||
|
// Shadows
|
||||||
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
// Radius
|
||||||
|
--radius-sm: 0.25rem;
|
||||||
|
--radius-md: 0.5rem;
|
||||||
|
--radius-lg: 0.75rem;
|
||||||
|
|
||||||
|
// Font sizes
|
||||||
|
--font-size-xs: 0.75rem;
|
||||||
|
--font-size-sm: 0.875rem;
|
||||||
|
--font-size-md: 1rem;
|
||||||
|
--font-size-lg: 1.125rem;
|
||||||
|
|
||||||
|
// Z-index
|
||||||
|
--z-index-modal: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode
|
||||||
|
[data-mode="dark"] {
|
||||||
|
--color-bg-primary: #111827;
|
||||||
|
--color-bg-secondary: #1f2937;
|
||||||
|
--color-bg-hover: #374151;
|
||||||
|
--color-border-primary: #374151;
|
||||||
|
--color-border-hover: #4b5563;
|
||||||
|
--color-text-primary: #f9fafb;
|
||||||
|
--color-text-secondary: #9ca3af;
|
||||||
|
--color-text-muted: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global styles
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
transition: background-color 200ms, color 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
32
tsconfig.json
Normal file
32
tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/out-tsc",
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": false,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"lib": ["ES2022", "dom"],
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"paths": {
|
||||||
|
"@sda/timeline-elements-ui": ["../timeline-elements-ui/src/index.ts"],
|
||||||
|
"@sda/base-ui": ["../../sda-frontend/libs/base-ui/src/index.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user