Add new components, styles, and services for user management and navigation

This commit is contained in:
liosha84
2025-06-25 13:44:35 +03:00
parent 9b7465d3dc
commit 2ae1871330
125 changed files with 12940 additions and 1610 deletions
+16 -14
View File
@@ -19,8 +19,7 @@
"options": {
"browser": "src/main.ts",
"polyfills": [
"zone.js",
"@angular/localize/init"
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
@@ -28,15 +27,22 @@
{
"glob": "**/*",
"input": "public"
},
{
"glob": "**/*",
"input": "src/assets/",
"output": "/assets/"
}
],
"styles": [
{
"input": "node_modules/@progress/kendo-theme-default/dist/all.css"
},
"@angular/material/prebuilt-themes/cyan-orange.css",
"@angular/material/prebuilt-themes/azure-blue.css",
"src/styles.scss"
]
],
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
}
},
"configurations": {
"production": {
@@ -44,7 +50,7 @@
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "5MB"
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
@@ -82,8 +88,7 @@
"options": {
"polyfills": [
"zone.js",
"zone.js/testing",
"@angular/localize/init"
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
@@ -94,10 +99,7 @@
}
],
"styles": [
{
"input": "node_modules/@progress/kendo-theme-default/dist/all.css"
},
"@angular/material/prebuilt-themes/cyan-orange.css",
"@angular/material/prebuilt-themes/azure-blue.css",
"src/styles.scss"
]
}
+284 -1170
View File
File diff suppressed because it is too large Load Diff
+6 -25
View File
@@ -11,43 +11,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^20.0.3",
"@angular/animations": "^20.0.4",
"@angular/cdk": "^20.0.3",
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/localize": "^20.0.0",
"@angular/material": "^20.0.3",
"@angular/platform-browser": "^20.0.0",
"@angular/platform-browser-dynamic": "^20.0.3",
"@angular/platform-server": "^20.0.0",
"@angular/router": "^20.0.0",
"@angular/ssr": "^20.0.2",
"@ant-design/icons-angular": "19.0.0",
"@ng-bootstrap/ng-bootstrap": "19.0.0",
"@popperjs/core": "2.11.8",
"@primeng/themes": "^19.1.3",
"@progress/kendo-angular-buttons": "19.1.1",
"@progress/kendo-angular-common": "19.1.1",
"@progress/kendo-angular-dialog": "19.1.1",
"@progress/kendo-angular-dropdowns": "19.1.1",
"@progress/kendo-angular-editor": "^19.1.1",
"@progress/kendo-angular-icons": "19.1.1",
"@progress/kendo-angular-indicators": "^19.1.1",
"@progress/kendo-angular-inputs": "19.1.1",
"@progress/kendo-angular-intl": "19.1.1",
"@progress/kendo-angular-l10n": "19.1.1",
"@progress/kendo-angular-label": "19.1.1",
"@progress/kendo-angular-layout": "19.1.1",
"@progress/kendo-angular-navigation": "19.1.1",
"@progress/kendo-angular-popup": "19.1.1",
"@progress/kendo-angular-progressbar": "19.1.1",
"@progress/kendo-angular-toolbar": "19.1.1",
"@progress/kendo-angular-treeview": "19.1.1",
"@progress/kendo-angular-utils": "^19.1.1",
"@progress/kendo-drawing": "^1.21.0",
"@progress/kendo-licensing": "^1.5.0",
"@progress/kendo-svg-icons": "^4.0.0",
"@progress/kendo-theme-default": "11.0.2",
"bootstrap": "5.3.6",
"express": "^5.1.0",
"ngx-scrollbar": "18.0.0",
"primeng": "^19.1.3",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
@@ -0,0 +1,38 @@
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
<p>admin-welcome.component works!</p>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminWelcomeComponent } from './admin-welcome.component';
describe('AdminWelcomeComponent', () => {
let component: AdminWelcomeComponent;
let fixture: ComponentFixture<AdminWelcomeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AdminWelcomeComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AdminWelcomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-admin-welcome.component',
imports: [],
templateUrl: './admin-welcome.component.html',
styleUrl: './admin-welcome.component.scss'
})
export class AdminWelcomeComponent {
}
@@ -0,0 +1,50 @@
<!--<p>admin.component works!</p>-->
<app-navigation
class="pc-sidebar"
[ngClass]="{
'navbar-collapsed': navCollapsed,
'mob-open': navCollapsedMob
}"
(NavCollapse)="this.navCollapsed = !this.navCollapsed"
/>
<app-nav-bar (NavCollapsedMob)="navMobClick()" (NavCollapse)="this.navCollapsed = !this.navCollapsed"/>
<div class="pc-container">
<div class="coded-wrapper">
<div class="coded-content">
<div class="coded-inner-content">
<app-breadcrumb />
<div class="main-body">
<div class="page-wrapper">
<router-outlet />
</div>
</div>
</div>
</div>
</div>
<div class="pc-menu-overlay" (click)="closeMenu()" (keydown)="handleKeyDown($event)" tabindex="0"></div>
</div>
<!--<div class="pc-container">
<div class="coded-wrapper">
<div class="coded-content">
<div class="coded-inner-content">
<app-breadcrumb />
<div class="main-body">
<div class="page-wrapper">
<router-outlet></router-outlet>
</div>
</div>
</div>
</div>
</div>
<div class="pc-menu-overlay" (click)="closeMenu()" (keydown)="handleKeyDown($event)" tabindex="0"></div>
</div>-->
@@ -0,0 +1,22 @@
.example-container {
width: 500px;
height: 300px;
border: 1px solid rgba(0, 0, 0, 0.5);
}
.example-sidenav-content {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
.example-sidenav {
padding: 20px;
}
.pc-sidebar{
top: 120px;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminComponent } from './admin.component';
describe('AdminComponent', () => {
let component: AdminComponent;
let fixture: ComponentFixture<AdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AdminComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,112 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
import {BreadcrumbComponent} from "../../components/breadcrumb/breadcrumb.component";
import {NavBarComponent} from "../../layouts/admin-layout/nav-bar/nav-bar.component";
import {NavigationComponent} from "../../layouts/admin-layout/navigation/navigation.component";
import {Router, RouterLink, RouterOutlet} from "@angular/router";
import {DOCUMENT, NgClass, NgStyle} from '@angular/common';
import {MatDrawerContainer, MatSidenavModule} from '@angular/material/sidenav';
import {MatButton} from '@angular/material/button';
import { ScrollPanelModule } from 'primeng/scrollpanel';
import {MenuItem} from 'primeng/api';
import {PanelMenu} from 'primeng/panelmenu';
@Component({
selector: 'app-admin.component',
imports: [
BreadcrumbComponent,
NavBarComponent,
NavigationComponent,
RouterOutlet,
NgClass,
MatDrawerContainer,
MatSidenavModule,
ScrollPanelModule,
MatButton,
RouterLink,
PanelMenu,
NgStyle,
],
schemas:[CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './admin.component.html',
styleUrl: './admin.component.scss'
})
export class AdminComponent implements OnInit {
private document = inject<Document>(DOCUMENT);
height = 0;
items: MenuItem[] | undefined;
constructor(private router: Router) {
this.height = 0
}
// public props
navCollapsed: boolean = false;
navCollapsedMob: boolean = false;
// public method
navMobClick() {
if (this.navCollapsedMob && !document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
this.navCollapsedMob = !this.navCollapsedMob;
setTimeout(() => {
this.navCollapsedMob = !this.navCollapsedMob;
}, 100);
} else {
this.navCollapsedMob = !this.navCollapsedMob;
}
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('navbar-collapsed')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('navbar-collapsed');
}
}
handleKeyDown(event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.closeMenu();
}
}
closeMenu() {
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('mob-open');
}
}
ngOnInit() {
if(this.document.defaultView !== null || this.document.defaultView !== undefined) {
// @ts-ignore
this.height = this.document.defaultView.innerHeight;
}
else
this.height = 0;
this.items = [
{
label: 'Admin',
icon: 'pi pi-palette',
command: () => {
this.router.navigate(['/main/admin/welcome']);
},
items: [
{
label: 'Settings',
icon: 'pi pi-eraser',
// route: '/main/admin/settings'
command: () => {
this.router.navigate(['/main/admin/settings']);
}
},
{
label: 'System',
icon: 'pi pi-heart',
route: '/main/admin/system'
}
]
}
];
}
}
@@ -0,0 +1,16 @@
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {mainRouting} from '../main-module/main.routing';
import {MainComponent} from '../main-module/main.component/main.component';
import {RouterModule} from '@angular/router';
import {AdminComponent} from './admin.component/admin.component';
import {adminRouting} from './admin.routing';
import { ScrollPanelModule } from 'primeng/scrollpanel';
@NgModule({
declarations: [
],
imports: [adminRouting, AdminComponent,ScrollPanelModule],
exports: [RouterModule],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
})
export class AdminModule {}
@@ -0,0 +1,27 @@
import {RouterModule, Routes} from '@angular/router';
import {AdminComponent} from './admin.component/admin.component';
const ADMIN_ROUTES: Routes = [
{
path: '',
component: AdminComponent,
children: [
{
path: 'welcome',
loadComponent: () => import('../admin-module/admin-welcome.component/admin-welcome.component').then((c) => c.AdminWelcomeComponent),
},
{
path: 'settings',
loadComponent: () => import('../admin-module/settings.component/settings.component').then((c) => c.SettingsComponent)
},
{
path: 'system',
loadComponent: () => import('../admin-module/system.component/system.component').then((c) => c.SystemComponent)
}
]
}
];
export const adminRouting = RouterModule.forChild(ADMIN_ROUTES);
@@ -0,0 +1 @@
<p>settings.component works!</p>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsComponent } from './settings.component';
describe('SettingsComponent', () => {
let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SettingsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-settings.component',
imports: [],
templateUrl: './settings.component.html',
styleUrl: './settings.component.scss'
})
export class SettingsComponent {
}
@@ -0,0 +1,107 @@
<mat-card>
<mat-card-content>
<mat-divider></mat-divider>
<mat-tab-group>
<mat-tab label="Custom beans">
Content 1
<table mat-table
[dataSource]="customBeans" multiTemplateDataRows="true" class="mat-elevation-z8">
@for (column of displayedColumns; track column) {
<ng-container matColumnDef="{{column}}">
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
<td mat-cell *matCellDef="let element ; let k = dataIndex;">{{element[column]}}</td>
</ng-container>
}
<ng-container matColumnDef="expand">
<th mat-header-cell *matHeaderCellDef aria-label="row actions">&nbsp;</th>
<td mat-cell *matCellDef="let element">
<button
matIconButton
aria-label="expand row"
(click)="toggle(element); $event.stopPropagation()"
class="example-toggle-button"
[class.example-toggle-button-expanded]="isExpanded(element)">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</td>
</ng-container>
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
<div class="example-element-detail-wrapper"
[class.example-element-detail-wrapper-expanded]="isExpanded(element)">
<div class="example-element-detail">
<mat-list role="list">
<mat-list-item role="listitem">Name: {{element.name}}</mat-list-item>
<mat-list-item role="listitem">Type: {{element.type}}</mat-list-item>
</mat-list>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
class="example-element-row"
[class.example-expanded-row]="isExpanded(element)"
(click)="toggle(element)">
</tr>
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
</table>
</mat-tab>
<mat-tab label="All beans">
Content 2
<table mat-table
[dataSource]="allBeans" multiTemplateDataRows="true" class="mat-elevation-z8">
@for (column of displayedColumns; track column) {
<ng-container matColumnDef="{{column}}">
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
<td mat-cell *matCellDef="let element ; let k = dataIndex;">{{element[column]}}</td>
</ng-container>
}
<ng-container matColumnDef="expand">
<th mat-header-cell *matHeaderCellDef aria-label="row actions">&nbsp;</th>
<td mat-cell *matCellDef="let element">
<button
matIconButton
aria-label="expand row"
(click)="toggle(element); $event.stopPropagation()"
class="example-toggle-button"
[class.example-toggle-button-expanded]="isExpanded(element)">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</td>
</ng-container>
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
<div class="example-element-detail-wrapper"
[class.example-element-detail-wrapper-expanded]="isExpanded(element)">
<div class="example-element-detail">
<mat-list role="list">
<mat-list-item role="listitem">Name: {{element.name}}</mat-list-item>
<mat-list-item role="listitem">Type: {{element.type}}</mat-list-item>
</mat-list>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
class="example-element-row"
[class.example-expanded-row]="isExpanded(element)"
(click)="toggle(element)">
</tr>
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
</table>
</mat-tab>
</mat-tab-group>
</mat-card-content>
</mat-card>
@@ -0,0 +1,113 @@
mat-card{
//margin: 20px;
background-color: rgba(240, 248, 255, 0.7);
backdrop-filter: blur(8px);
}
mat-card-title{
color: cyan;
}
mat-card-subtitle{
color: #009dff;
}
mat-tab{
color:black;
}
table {
width: 100%;
margin-top: 20px;
color:lightgrey;
}
mat-list{
//background-color: #1389d3;
color:black;
width: 100%;
}
mat-list-item{
margin: 10px 10px 10px 10px;
background-color: #447694;
color: #009dff;
}
tr.example-detail-row {
height: 0;
background: #fffdfd;
}
tr.example-element-row {
cursor: pointer;
color: #1389d3;
background: #fffdfd;
}
tr.example-element-row:not(.example-expanded-row):hover {
background: whitesmoke;
}
tr.example-element-row:not(.example-expanded-row):active {
background: #efefef;
}
.example-element-row td {
border-bottom-width: 0;
}
.example-element-detail-wrapper {
overflow: hidden;
display: grid;
grid-template-rows: 0fr;
grid-template-columns: 100%;
transition: grid-template-rows 225ms cubic-bezier(0.4, 0, 0.2, 1);
}
.example-element-detail-wrapper-expanded {
grid-template-rows: 1fr;
}
.example-element-detail {
display: flex;
min-height: 0;
}
.example-element-diagram {
min-width: 80px;
border: 2px solid black;
padding: 8px;
font-weight: lighter;
margin: 8px 0;
height: 104px;
}
.example-element-symbol {
font-weight: bold;
font-size: 40px;
line-height: normal;
}
.example-element-description {
padding: 16px;
}
.example-element-description-attribution {
opacity: 0.5;
}
.example-toggle-button {
transition: transform 225ms cubic-bezier(0.4, 0, 0.2, 1);
}
.example-toggle-button-expanded {
transform: rotate(180deg);
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SystemComponent } from './system.component';
describe('SystemComponent', () => {
let component: SystemComponent;
let fixture: ComponentFixture<SystemComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SystemComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SystemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,138 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
import {MatDivider} from '@angular/material/divider';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell, MatHeaderCellDef,
MatHeaderRow, MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable
} from '@angular/material/table';
import {MatList, MatListItem} from '@angular/material/list';
import {MatIconButton} from '@angular/material/button';
import {SystemService} from '../../services/system.service';
import {Bean} from '../../models/Bean';
import {MatIcon} from '@angular/material/icon';
@Component({
selector: 'app-system.component',
imports: [
MatCard,
MatCardHeader,
MatCardContent,
MatDivider,
MatTabGroup,
MatTab,
MatTable,
MatColumnDef,
MatIcon,
MatCell,
MatCellDef,
MatList,
MatListItem,
MatRow,
MatHeaderRow,
MatRowDef,
MatHeaderCell,
MatHeaderCellDef,
MatIconButton,
MatIcon,
MatHeaderRowDef
],
templateUrl: './system.component.html',
styleUrl: './system.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SystemComponent {
customBeans:Bean[] = [] ;
allBeans:Bean[] = [] ;
displayedColumns: string[] = ['shortName', 'typeShortName', 'scope'];
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
expandedElement: Bean | null = null;
resultsLength = 0;
isLoadingResults = true;
isRateLimitReached = false;
constructor(private systemService: SystemService) { }
ngOnInit(): void {
this.retrieveBeans();
}
retrieveBeans(): void {
this.systemService.getCustomBeans()
.subscribe(
(data: Bean[]) => {
this.customBeans = data;
console.log(data);
},
(error: any) => {
console.log(error);
});
this.systemService.getAllBeans()
.subscribe(
(data: Bean[]) => {
this.allBeans = data;
console.log(data);
},
(error: any) => {
console.log(error);
});
}
/** Checks whether an element is expanded. */
isExpanded(element: Bean) {
return this.expandedElement === element;
}
/** Toggles the expanded state of an element. */
toggle(element: Bean) {
this.expandedElement = this.isExpanded(element) ? null : element;
}
//ngAfterViewInit() {
//this.exampleDatabase = new ExampleHttpDatabase(this._httpClient);
// If the user changes the sort order, reset back to the first page.
/* this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));
merge(this.sort.sortChange, this.paginator.page)
.pipe(
startWith({}),
switchMap(() => {
this.isLoadingResults = true;
return this.exampleDatabase!.getRepoIssues(
this.sort.active,
this.sort.direction,
this.paginator.pageIndex,
).pipe(catchError(() => observableOf(null)));
}),
map(data => {
// Flip flag to show that loading has finished.
this.isLoadingResults = false;
this.isRateLimitReached = data === null;
if (data === null) {
return [];
}
// Only refresh the result length if there is new data. In case of rate
// limit errors, we do not want to reset the paginator to zero, as that
// would prevent users from re-triggering requests.
this.resultsLength = data.total_count;
return data.items;
}),
)
.subscribe(data => (this.data = data));*/
}
@@ -1,35 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
import { BoardAdminComponent } from './components/board-admin/board-admin.component';
import { BoardModeratorComponent } from './components/board-moderator/board-moderator.component';
import { BoardUserComponent } from './components/board-user/board-user.component';
import { ProfileComponent } from './components/profile/profile.component';
import { RegisterComponent } from './components/register/register.component';
import { LoginComponent } from './components/login/login.component';
import { HomeComponent } from './components/home/home.component';
import {SystemComponent} from './components/system.component/system.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'profile', component: ProfileComponent },
{ path: 'user', component: BoardUserComponent },
{ path: 'mod', component: BoardModeratorComponent },
{ path: 'admin', component: BoardAdminComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'tutorials', component: TutorialsListComponent },
{ path: 'tutorials/:id', component: TutorialDetailsComponent },
{ path: 'add', component: AddTutorialComponent },
{ path: 'system', component: SystemComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {useHash: true})],
exports: [RouterModule]
})
export class AppRoutingModule { }
+16 -5
View File
@@ -1,16 +1,27 @@
import {Component, inject, OnInit} from '@angular/core';
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
import { TokenStorageService } from './services/token-storage.service';
import {MatDialog} from "@angular/material/dialog";
import {DialogComponent} from "./components/dialog/dialog.component";
import {AuthService} from "./services/auth.service";
import {Subscription} from "rxjs";
import {EventBusService} from "./_shared/event-bus.service";
import {RouterLink, RouterOutlet} from '@angular/router';
import {MatButton} from '@angular/material/button';
import {MatToolbar} from '@angular/material/toolbar';
import {MatIcon} from '@angular/material/icon';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: false
selector: 'app-root',
templateUrl: './app.component.html',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
RouterOutlet,
MatIcon,
MatButton,
RouterLink,
MatToolbar
],
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
readonly dialog = inject(MatDialog);
+12
View File
@@ -0,0 +1,12 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(withRoutes(serverRoutes))
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
+28
View File
@@ -0,0 +1,28 @@
import {
ApplicationConfig,
importProvidersFrom,
provideBrowserGlobalErrorListeners,
provideZoneChangeDetection
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import {AppModule} from './app.module';
import {providePrimeNG} from 'primeng/config';
import Aura from '@primeng/themes/aura';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes), provideClientHydration(withEventReplay()),
importProvidersFrom(AppModule),
providePrimeNG({
theme: {
preset: Aura
}
})
]
};
+1
View File
@@ -0,0 +1 @@
<router-outlet></router-outlet>
+27 -105
View File
@@ -1,118 +1,40 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { AppComponent } from './app.component';
import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
import { HomeComponent } from './components/home/home.component';
import { ProfileComponent } from './components/profile/profile.component';
import { BoardAdminComponent } from './components/board-admin/board-admin.component';
import { BoardModeratorComponent } from './components/board-moderator/board-moderator.component';
import { BoardUserComponent } from './components/board-user/board-user.component';
import { authInterceptorProviders } from './helpers/auth.interceptor';
import {provideHttpClient, withInterceptorsFromDi} from "@angular/common/http";
import {provideAnimationsAsync} from "@angular/platform-browser/animations/async";
import {MatToolbar} from "@angular/material/toolbar";
import {MatAnchor, MatButton} from "@angular/material/button";
import {MatIcon} from "@angular/material/icon";
import {MatChipGrid, MatChipInput, MatChipListbox, MatChipOption, MatChipRow} from "@angular/material/chips";
import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
import {MatFormField} from "@angular/material/form-field";
import {MatLabel} from '@angular/material/form-field';
import {
MatCard, MatCardActions,
MatCardContent,
MatCardHeader,
MatCardImage,
MatCardSubtitle,
MatCardTitle
} from '@angular/material/card';
import {MatInput} from '@angular/material/input';
import {EditorComponent} from '@progress/kendo-angular-editor';
import {ArticleComments} from './components/article-comments/article-comments';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
import {providePrimeNG} from 'primeng/config';
import Aura from '@primeng/themes/aura';
import {Button} from 'primeng/button';
import {ScrollPanelModule} from 'primeng/scrollpanel';
import {Splitter} from 'primeng/splitter';
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import { BrowserModule }
from '@angular/platform-browser';
import { BrowserAnimationsModule }
from '@angular/platform-browser/animations';
import { MatIconModule } from '@angular/material/icon';
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {MainComponent} from './main-module/main.component/main.component';
import {authInterceptorProviders} from './helpers/auth.interceptor';
import {AdminComponent} from './admin-module/admin.component/admin.component';
import {AdminWelcomeComponent} from './admin-module/admin-welcome.component/admin-welcome.component';
import {SettingsComponent} from './admin-module/settings.component/settings.component';
import {SystemComponent} from './admin-module/system.component/system.component';
import {appRouting} from './app.routes';
import {App} from './app';
@NgModule({
declarations: [
AppComponent,
TutorialsListComponent,
TutorialDetailsComponent,
AddTutorialComponent,
RegisterComponent,
HomeComponent,
ProfileComponent,
BoardAdminComponent,
BoardModeratorComponent,
BoardUserComponent
],
bootstrap: [AppComponent],
imports: [
appRouting,
MainComponent,
BrowserModule,
AppRoutingModule,
FormsModule,
LoginComponent,
MatToolbar,
MatButton,
MatIcon,
MatAnchor,
MatChipGrid,
MatChipRow,
MatChipInput,
MatAutocompleteTrigger,
MatAutocomplete,
MatOption,
MatFormField,
MatChipListbox,
MatChipOption,
ReactiveFormsModule,
MatCardContent,
MatCardSubtitle,
MatCardTitle,
MatCard,
MatCardHeader,
MatCardImage,
MatCardActions,
MatInput,
MatLabel,
EditorComponent,
ArticleComments,
MatProgressSpinner,
Button,
ScrollPanelModule,
Splitter
MatIconModule,
BrowserAnimationsModule,
AdminComponent,
AdminWelcomeComponent,
SettingsComponent,
SystemComponent
],
providers: [
authInterceptorProviders,
provideHttpClient(withInterceptorsFromDi()),
provideAnimationsAsync(),
providePrimeNG({
theme: {
preset: Aura,
options: {darkModeSelector: '.p-dark'}
}
})
] })
authInterceptorProviders,provideHttpClient(withInterceptorsFromDi())],
bootstrap: [],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
@@ -0,0 +1,8 @@
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Prerender
}
];
+20
View File
@@ -0,0 +1,20 @@
import {RouterModule, Routes} from '@angular/router';
import {AdminComponent} from './layouts/admin-layout/admin-layout.component';
import {MainComponent} from './main-module/main.component/main.component';
import {NgModule} from '@angular/core';
export const routes: Routes = [
{
path: '',
redirectTo: 'main/home',
pathMatch: 'full'
},
{
path: 'main',
loadChildren: () =>
import('./main-module/main.module').then((m) => m.MainModule),
}
];
export const appRouting = RouterModule.forRoot(routes);
View File
+23
View File
@@ -0,0 +1,23 @@
import { TestBed } from '@angular/core/testing';
import { App } from './app';
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [App],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(App);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it('should render title', () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, jambotron-ui');
});
});
+16
View File
@@ -0,0 +1,16 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.html',
imports: [
RouterOutlet
],
styleUrl: './app.scss'
})
export class App {
protected title = 'jambotron-ui';
constructor() { }
}
@@ -44,7 +44,8 @@
<input matInput required
[(ngModel)]="tutorial.title">
</mat-form-field>
<kendo-editor [(ngModel)]="tutorial.description" ></kendo-editor>
<!-- <kendo-editor [(ngModel)]="tutorial.description" ></kendo-editor>-->
<textarea matInput required [(ngModel)]="tutorial.description"></textarea>
</mat-card-content>
<mat-card-actions>
<button matButton (click)="saveTutorial()" *ngIf="!submitted">Save</button>
@@ -1,12 +1,32 @@
import { Component, OnInit } from '@angular/core';
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core';
import {Tutorial} from '../../models/tutorial.model';
import {TutorialService} from '../../services/tutorial.service';
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
import {FormsModule} from '@angular/forms';
import {MatButton} from '@angular/material/button';
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {NgIf} from '@angular/common';
@Component({
selector: 'app-add-tutorial',
templateUrl: './add-tutorial.component.html',
styleUrls: ['./add-tutorial.component.scss'],
standalone: false
imports: [
MatCardActions,
FormsModule,
MatCard,
MatCardHeader,
MatCardContent,
MatFormField,
MatLabel,
MatButton,
MatInput,
NgIf,
MatLabel,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AddTutorialComponent implements OnInit {
@@ -1,25 +1,45 @@
import {Component, computed, model, OnInit} from '@angular/core';
import {Component, computed, CUSTOM_ELEMENTS_SCHEMA, model, OnInit} from '@angular/core';
import {User} from "../../models/user.model";
import {MatChipInputEvent} from "@angular/material/chips";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRow} from "@angular/material/chips";
import {
MatAutocomplete,
MatAutocompleteSelectedEvent,
MatAutocompleteTrigger,
MatOption
} from "@angular/material/autocomplete";
import {COMMA, ENTER} from "@angular/cdk/keycodes";
import {AsyncPipe} from "@angular/common";
import {FormControl} from "@angular/forms";
import {AsyncPipe, NgForOf} from "@angular/common";
import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {Observable, startWith} from "rxjs";
import {map} from "rxjs/operators";
import {UserService} from '../../services/user.service';
import {RolesService} from '../../services/roles.service';
import {Role} from '../../models/role';
import {MatIcon} from '@angular/material/icon';
import {MatFormField} from '@angular/material/form-field';
@Component({
selector: 'app-board-admin',
templateUrl: './board-admin.component.html',
styleUrls: ['./board-admin.component.scss'],
standalone: false,
selector: 'app-board-admin',
templateUrl: './board-admin.component.html',
styleUrls: ['./board-admin.component.scss'],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
MatAutocomplete,
ReactiveFormsModule,
MatOption,
NgForOf,
MatFormField,
MatChipGrid,
MatChipRow,
MatIcon,
MatChipInput,
MatAutocompleteTrigger
]
})
export class BoardAdminComponent implements OnInit {
private gridApi: any;
@@ -0,0 +1,16 @@
<div class="card" [ngClass]="cardClass()">
@if (showHeader()) {
<div class="card-header d-flex align-items-center justify-content-between" [ngClass]="headerClass()">
<div>
<h5>{{ cardTitle() }}</h5>
<ng-container *ngTemplateOutlet="headerTitleTemplate"></ng-container>
</div>
<ng-container *ngTemplateOutlet="headerOptionsTemplate"></ng-container>
</div>
}
@if (showContent()) {
<div class="card-body" [ngClass]="blockClass()" [style.padding.px]="padding()">
<ng-content></ng-content>
</div>
}
</div>
@@ -0,0 +1,58 @@
// Angular import
import { Component, ContentChild, ElementRef, TemplateRef, input } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-card',
standalone: true,
imports: [CommonModule],
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss']
})
export class CardComponent {
// public props
/**
* Title of card. It will be visible at left side of card header
*/
cardTitle = input<string>();
/**
* Class to be applied at card level
*/
cardClass = input<string>();
/**
* To hide content from card
*/
showContent = input(true);
/**
* Class to be applied at card content.
*/
blockClass = input<string>();
/**
* Class to be applied on card header
*/
headerClass = input<string>();
/**
* To hide header from card
*/
showHeader = input(true);
/**
* padding around card content. default in px
*/
padding = input(20); // set default to 24 px
/**
* Template reference of header actions on custom header
*/
@ContentChild('headerOptionsTemplate') headerOptionsTemplate!: TemplateRef<ElementRef>;
/**
* Template reference of header actions besides title at left
*/
@ContentChild('headerTitleTemplate') headerTitleTemplate!: TemplateRef<ElementRef>;
}
@@ -14,7 +14,8 @@ import {MatFormField, MatLabel, MatSuffix} from "@angular/material/form-field";
import {MatInput} from "@angular/material/input";
import {FormsModule} from "@angular/forms";
import {MatIcon} from "@angular/material/icon";
import {DialogData} from "../../app.component";
import {DialogData} from '../../main-module/main.component/main.component';
@@ -0,0 +1,51 @@
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
<p>home.component works!</p>
@@ -8,12 +8,10 @@ describe('HomeComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HomeComponent ]
imports: [HomeComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-home.component',
imports: [],
templateUrl: './home.component.html',
styleUrl: './home.component.scss'
})
export class HomeComponent {
}
@@ -1,145 +0,0 @@
<p-splitter [style]="{ height: '300px' }" styleClass="mb-8" layout="vertical">
<ng-template #panel>
<div class="flex items-center justify-center h-full">Panel 1</div>
</ng-template>
<ng-template #panel>
<div class="flex items-center justify-center h-full">Panel 2</div>
<p-scrollPanel [style]="{ width: '100%', height: '150px' }">
<p>
Lorem ipsum dolor sit amet...
</p>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium...
</p>
<p class="m-0">
At vero eos et accusamus et iusto odio dignissimos...
dfsd
f
sdf
dsf
sd
f
sd
fs
fs
fsfs
fs
fs
fs
fs
fs
</p>
<app-article-comments />
</p-scrollPanel>
</ng-template>
</p-splitter>
<p-scrollPanel [style]="{ width: '100%', height: '150px' }">
<p>
Lorem ipsum dolor sit amet...
</p>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium...
</p>
<p class="m-0">
At vero eos et accusamus et iusto odio dignissimos...
dfsd
f
sdf
dsf
sd
f
sd
fs
fs
fsfs
fs
fs
fs
fs
fs
</p>
<app-article-comments />
</p-scrollPanel>
<!--
@defer (on viewport(container); prefetch on idle) {
<app-article-comments></app-article-comments>
} @loading {
<mat-progress-spinner></mat-progress-spinner>
loading
} @placeholder {
<div class="skeleton-loader"></div>
<app-article-comments></app-article-comments>
placeholder
} @error {
Error
}
<div #container>
<h1>How I feel about Angular</h1>
<article>
<p>
Angular is my favorite framework, and
this is why. Angular has the coolest
deferrable view feature that makes defer
loading content the easiest and most
ergonomic it could possibly be.
</p>
</article>
@defer {
<app-article-comments />
} @placeholder (minimum 1s) {
<p>Placeholder for comments</p>
} @loading (minimum 1s; after 500ms) {
<p>Loading comments...</p>
} @error {
<p>Failed to load comments</p>
}
</div>
<ng-container [ngComponentOutlet]="profileComponent()" />
-->
<mat-card #showComments appearance="outlined" >
<mat-card-header>
<mat-card-title>Tutorials</mat-card-title>
</mat-card-header>
<mat-card-content>
</mat-card-content>
</mat-card>
@defer (on hover; on interaction(showComments)) {
<app-article-comments />
} @placeholder ( ;minimum 1s) {
<p>Placeholder for comments</p>
} @loading (minimum 1s; after 500ms) {
<p>Loading comments...</p>
} @error {
<p>Failed to load comments</p>
}
@for (tutorial of tutorials; track $index){
<mat-card appearance="outlined" class="tutorial-card">
<mat-card-content [innerHTML]="tutorial.description">
</mat-card-content>
</mat-card>
}
@@ -1,10 +0,0 @@
mat-card{
margin: 20px;
background-color: rgba(0, 0, 0, 0.7);
}
mat-card-title{
color: cyan;
}
.tutorial-card{
color: white;
}
@@ -1,38 +0,0 @@
import {Component, computed, OnInit} from '@angular/core';
import {TutorialService} from '../../services/tutorial.service';
import {Tutorial} from '../../models/tutorial.model';
import {ArticleComments} from '../article-comments/article-comments';
import {AddTutorialComponent} from '../add-tutorial/add-tutorial.component';
import { ScrollPanelModule } from 'primeng/scrollpanel';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
standalone: false
})
export class HomeComponent implements OnInit {
content?: string;
tutorials?: Tutorial[];
constructor(private tutorialService: TutorialService) { }
ngOnInit(): void {
this.retrieveTutorials();
}
retrieveTutorials(): void {
this.tutorialService.getAll()
.subscribe(
data => {
this.tutorials = data;
console.log(data);
},
error => {
console.log(error);
});
}
profileComponent = computed(() => AddTutorialComponent);
}
@@ -1,12 +1,16 @@
import { Component, OnInit } from '@angular/core';
import {TokenStorageService} from '../../services/token-storage.service';
import {NgForOf, NgIf} from '@angular/common';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.scss'],
standalone: false
selector: 'app-profile',
templateUrl: './profile.component.html',
imports: [
NgForOf,
NgIf
],
styleUrls: ['./profile.component.scss']
})
export class ProfileComponent implements OnInit {
currentUser: any;
@@ -1,12 +1,17 @@
import { Component, OnInit } from '@angular/core';
import {AuthService} from '../../services/auth.service';
import {FormsModule} from '@angular/forms';
import {NgIf} from '@angular/common';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss'],
standalone: false
selector: 'app-register',
templateUrl: './register.component.html',
imports: [
FormsModule,
NgIf
],
styleUrls: ['./register.component.scss']
})
export class RegisterComponent implements OnInit {
form: any = {
@@ -3,13 +3,18 @@ import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {Tutorial} from '../../models/tutorial.model';
import {TutorialService} from '../../services/tutorial.service';
import {FormsModule} from '@angular/forms';
import {NgIf} from '@angular/common';
@Component({
selector: 'app-tutorial-details',
templateUrl: './tutorial-details.component.html',
styleUrls: ['./tutorial-details.component.scss'],
standalone: false
selector: 'app-tutorial-details',
templateUrl: './tutorial-details.component.html',
imports: [
FormsModule,
NgIf
],
styleUrls: ['./tutorial-details.component.scss']
})
export class TutorialDetailsComponent implements OnInit {
@@ -3,12 +3,19 @@ import { Component, OnInit } from '@angular/core';
import {Tutorial} from '../../models/tutorial.model';
import {TutorialService} from '../../services/tutorial.service';
import {FormsModule} from '@angular/forms';
import {RouterLink} from '@angular/router';
import {NgIf} from '@angular/common';
@Component({
selector: 'app-tutorials-list',
templateUrl: './tutorials-list.component.html',
styleUrls: ['./tutorials-list.component.scss'],
standalone: false
selector: 'app-tutorials-list',
templateUrl: './tutorials-list.component.html',
imports: [
FormsModule,
RouterLink,
NgIf
],
styleUrls: ['./tutorials-list.component.scss']
})
export class TutorialsListComponent implements OnInit {
@@ -0,0 +1,9 @@
@for (tutorial of tutorials; track $index){
<mat-card appearance="outlined" class="tutorial-card">
<mat-card-header>
<mat-card-title>{{tutorial.title}}</mat-card-title>
</mat-card-header>
<mat-card-content [innerHTML]="tutorial.description">
</mat-card-content>
</mat-card>
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TutorialsComponent } from './tutorials.component';
describe('TutorialsComponent', () => {
let component: TutorialsComponent;
let fixture: ComponentFixture<TutorialsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TutorialsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TutorialsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,39 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {Tutorial} from '../../models/tutorial.model';
import {TutorialService} from '../../services/tutorial.service';
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
@Component({
selector: 'app-tutorials.component',
imports: [
MatCard,
MatCardContent,
MatCardHeader
],
templateUrl: './tutorials.component.html',
styleUrl: './tutorials.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class TutorialsComponent {
tutorials?: Tutorial[];
constructor(private tutorialService: TutorialService) {
this.retrieveTutorials();
}
// ngOnInit(): void {
// this.retrieveTutorials();
// }
retrieveTutorials(): void {
this.tutorialService.getAll()
.subscribe(
data => {
this.tutorials = data;
console.log(data);
},
error => {
console.log(error);
});
}
}
@@ -0,0 +1,65 @@
<!--<p>main.component works!</p>-->
<mat-toolbar class="fixed-top">
<button mat-raised-button routerLink="/" href="#">
My App
</button>
<span class="menu-spacer"></span>
<button mat-raised-button routerLink="tutorials">
Tutorials
</button>
@if (isLoggedIn) {
<button mat-raised-button routerLink="add-tutorial" >
Add tutorial
</button>
}
<span class="menu-spacer"></span>
@if (showAdminBoard) {
<button mat-raised-button routerLink="admin/welcome">
<mat-icon>manage_accounts</mat-icon>
Admin Bord
</button>
}
@if (showAdminBoard) {
<button mat-raised-button routerLink="system" >
<mat-icon>settings_applications</mat-icon>
System
</button>
}
<span class="menu-spacer"></span>
@if (isLoggedIn) {
<button mat-raised-button routerLink="register">
<mat-icon>app_registration</mat-icon>
Register
</button>
}
@if (!isLoggedIn) {
<button mat-raised-button (click)="openDialog('100ms', '5ms')" >
<!-- <i antIcon type="bell" theme="outline"></i>-->
<!-- <mat-icon>login</mat-icon>-->
Login
</button>
}
@if (isLoggedIn) {
<button mat-raised-button routerLink="profile" >
<mat-icon>account_circle</mat-icon>
{{ username }}
</button>
<button mat-raised-button (click)="logout()">
<mat-icon>logout</mat-icon>
Logout
</button>
}
</mat-toolbar>
<!--<app-nav-bar (NavCollapsedMob)="navMobClick()" (NavCollapse)="this.navCollapsed = !this.navCollapsed" />-->
<div class="router_outlet_padding">
<router-outlet/>
</div>
@@ -0,0 +1,15 @@
mat-toolbar{
background-color: rgba(153,153,153,0.16);
backdrop-filter: blur(8px);
}
.menu-spacer {
flex: 1 1 auto;
}
.fixed-top {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1000; // Ensure toolbar stays above other content
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MainComponent } from './main.component';
describe('MainComponent', () => {
let component: MainComponent;
let fixture: ComponentFixture<MainComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MainComponent]
})
.compileComponents();
fixture = TestBed.createComponent(MainComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,163 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
//import {NavigationComponent} from '../navigation/navigation.component';
import {NgClass} from '@angular/common';
import {NavigationComponent} from '../../layouts/admin-layout/navigation/navigation.component';
import {RouterLink, RouterOutlet} from '@angular/router';
import {BreadcrumbComponent} from '../../components/breadcrumb/breadcrumb.component';
import {NavBarComponent} from '../../layouts/admin-layout/nav-bar/nav-bar.component';
import {MatToolbar} from '@angular/material/toolbar';
import {MatButton} from '@angular/material/button';
import {IconDirective} from '@ant-design/icons-angular';
import {MatDialog} from '@angular/material/dialog';
import {Subscription} from 'rxjs';
import {TokenStorageService} from '../../services/token-storage.service';
import {AuthService} from '../../services/auth.service';
import {EventBusService} from '../../_shared/event-bus.service';
import {DialogComponent} from '../../components/dialog/dialog.component';
@Component({
selector: 'app-main.component',
imports: [
NavBarComponent,
MatToolbar,
MatButton,
RouterLink,
IconDirective,
RouterOutlet
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
templateUrl: './main.component.html',
styleUrl: './main.component.scss'
})
export class MainComponent {
// public props
navCollapsed: boolean = false;
navCollapsedMob: boolean = false;
readonly dialog = inject(MatDialog);
//private authService = new AuthService(provideHttpClient())
public dialogData : DialogData = {password: "", username: ""};
private roles: string[] = [];
isLoggedIn = false;
showAdminBoard = false;
showModeratorBoard = false;
username?: string;
eventBusSub?: Subscription;
private errorMessage: any;
private isLoginFailed: boolean = false;
private storageService: TokenStorageService = inject(TokenStorageService);
private authService: AuthService = inject(AuthService);
private eventBusService: EventBusService = inject(EventBusService);
constructor(
) {}
ngOnInit(): void {
this.isLoggedIn = this.storageService.isLoggedIn();
if (this.isLoggedIn) {
const user = this.storageService.getUser();
this.roles = user.roles;
this.showAdminBoard = true;//this.roles.includes('ROLE_ADMIN');
this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
this.username = user.username;
}
this.eventBusSub = this.eventBusService.on('logout', () => {
this.logout();
});
}
// public method
navMobClick() {
if (this.navCollapsedMob && !document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
this.navCollapsedMob = !this.navCollapsedMob;
setTimeout(() => {
this.navCollapsedMob = !this.navCollapsedMob;
}, 100);
} else {
this.navCollapsedMob = !this.navCollapsedMob;
}
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('navbar-collapsed')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('navbar-collapsed');
}
}
handleKeyDown(event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.closeMenu();
}
}
closeMenu() {
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('mob-open');
}
}
logout(): void {
this.authService.logout().subscribe({
next: res => {
console.log(res);
this.storageService.clean();
window.location.reload();
},
error: err => {
console.log(err);
}
});
}
openDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
let dialogRef = this.dialog.open(DialogComponent, {
width: '350px',
enterAnimationDuration,
exitAnimationDuration,
data: {username: this.dialogData.username, password: this.dialogData.password}
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
if(result== null){
return;
}
this.dialogData = result;
this.authService.login(this.dialogData.username, this.dialogData.password).subscribe(
data => {
this.storageService.saveToken(data.accessToken);
this.storageService.saveUser(data);
this.isLoginFailed = false;
this.isLoggedIn = true;
let user = this.storageService.getUser();
this.roles = user.roles;
//this.username = user.username;
this.reloadPage();
},
err => {
this.errorMessage = err.error.message;
this.isLoginFailed = true;
}
);
});
}
reloadPage(): void {
window.location.reload();
}
}
export interface DialogData {
username: string;
password: string;
}
@@ -0,0 +1,13 @@
import {RouterModule} from '@angular/router';
import {MainComponent} from './main.component/main.component';
import {mainRouting} from './main.routing';
import {NgModule} from '@angular/core';
@NgModule({
declarations: [
],
imports: [mainRouting, MainComponent],
exports: [RouterModule],
})
export class MainModule {}
@@ -0,0 +1,44 @@
import {RouterModule, Routes} from '@angular/router';
import {HomeComponent} from '../components/home.component/home.component';
import {MainComponent} from './main.component/main.component';
import {TutorialsListComponent} from '../components/tutorials-list/tutorials-list.component';
import {TutorialsComponent} from '../components/tutorials.component/tutorials.component';
import {AddTutorialComponent} from '../components/add-tutorial/add-tutorial.component';
import {RegisterComponent} from '../components/register/register.component';
import {ProfileComponent} from '../components/profile/profile.component';
const MAIN_ROUTES: Routes =[
{
path: '',
component: MainComponent,
children: [
{
path: 'admin',
loadChildren: () =>
import('../admin-module/admin.module').then((m) => m.AdminModule),
},
{
path: 'home',
component: HomeComponent
},
{
path: 'tutorials',
component: TutorialsComponent
},
{
path: 'add-tutorial',
component: AddTutorialComponent
},
{
path: 'register',
component : RegisterComponent
},
{
path: 'profile',
component : ProfileComponent
}
],
}
];
export const mainRouting = RouterModule.forChild(MAIN_ROUTES);
@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
import {inject, Injectable} from '@angular/core';
import {DOCUMENT} from '@angular/common';
const TOKEN_KEY = 'auth-token';
const USER_KEY = 'auth-user';
@@ -7,28 +8,33 @@ const USER_KEY = 'auth-user';
providedIn: 'root'
})
export class TokenStorageService {
constructor() { }
private document = inject<Document>(DOCUMENT);
constructor() {
}
signOut(): void {
window.sessionStorage.clear();
this.document.defaultView?.sessionStorage.clear();
}
public saveToken(token: string): void {
window.sessionStorage.removeItem(TOKEN_KEY);
window.sessionStorage.setItem(TOKEN_KEY, token);
document.defaultView?.sessionStorage.removeItem(TOKEN_KEY);
document.defaultView?.sessionStorage.setItem(TOKEN_KEY, token);
}
public getToken(): string | null {
return window.sessionStorage.getItem(TOKEN_KEY);
public getToken(): string | null| undefined {
return document.defaultView?.sessionStorage.getItem(TOKEN_KEY);
}
public saveUser(user: any): void {
window.sessionStorage.removeItem(USER_KEY);
window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
document.defaultView?.sessionStorage.removeItem(USER_KEY);
document.defaultView?.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
}
public getUser(): any {
const user = window.sessionStorage.getItem(USER_KEY);
const user = document.defaultView?.sessionStorage.getItem(USER_KEY);
if (user) {
return JSON.parse(user);
}
@@ -37,11 +43,11 @@ export class TokenStorageService {
}
clean(): void {
window.sessionStorage.clear();
document.defaultView?.sessionStorage.clear();
}
public isLoggedIn(): boolean {
const user = window.sessionStorage.getItem(USER_KEY);
const user = this.document.defaultView?.sessionStorage?.getItem(USER_KEY);
if (user) {
return true;
}
View File
@@ -0,0 +1,3 @@
<svg width="9" height="16" viewBox="0 0 9 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.04102 0.125034C3.96989 0.125034 2.29102 1.80391 2.29102 3.87503V5.75003H0.0410156V8.75003H2.29102V15.875H5.29102V8.75003H7.91602L8.29102 5.75003H5.29102V4.25003C5.29102 3.42166 5.96264 2.75003 6.79102 2.75003H8.29102V0.245784C7.57514 0.171909 6.76139 0.123534 6.04102 0.125034Z" fill="#4267B2"/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.6871 6.53113H15.083V6.5H8.33301V9.5H12.5716C11.9533 11.2464 10.2916 12.5 8.33301 12.5C5.84788 12.5 3.83301 10.4851 3.83301 8C3.83301 5.51487 5.84788 3.5 8.33301 3.5C9.48013 3.5 10.5238 3.93275 11.3184 4.63962L13.4398 2.51825C12.1003 1.26987 10.3085 0.5 8.33301 0.5C4.19113 0.5 0.833008 3.85812 0.833008 8C0.833008 12.1419 4.19113 15.5 8.33301 15.5C12.4749 15.5 15.833 12.1419 15.833 8C15.833 7.49713 15.7813 7.00625 15.6871 6.53113Z" fill="#FFC107"/>
<path d="M1.69824 4.50913L4.16237 6.31625C4.82912 4.6655 6.44387 3.5 8.33349 3.5C9.48062 3.5 10.5242 3.93275 11.3189 4.63963L13.4402 2.51825C12.1007 1.26988 10.309 0.5 8.33349 0.5C5.45274 0.5 2.95449 2.12638 1.69824 4.50913Z" fill="#FF3D00"/>
<path d="M8.33312 15.5C10.2704 15.5 12.0306 14.7586 13.3615 13.553L11.0402 11.5888C10.2872 12.1591 9.35125 12.5 8.33312 12.5C6.38237 12.5 4.726 11.2561 4.102 9.52026L1.65625 11.4046C2.8975 13.8335 5.41825 15.5 8.33312 15.5Z" fill="#4CAF50"/>
<path d="M15.6871 6.53113H15.083V6.5H8.33301V9.5H12.5716C12.2746 10.3389 11.735 11.0622 11.039 11.5891C11.0394 11.5887 11.0398 11.5887 11.0401 11.5884L13.3614 13.5526C13.1971 13.7019 15.833 11.75 15.833 8C15.833 7.49713 15.7813 7.00625 15.6871 6.53113Z" fill="#1976D2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,5 @@
<svg width="405" height="809" viewBox="0 0 405 809" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M-358.39 358.707L-293.914 294.23L-293.846 294.163H-172.545L-220.81 342.428L-233.272 354.889L-282.697 404.314L-276.575 410.453L0.316589 687.328L283.33 404.314L233.888 354.889L230.407 351.391L173.178 294.163H294.48L294.547 294.23L345.082 344.765L404.631 404.314L0.316589 808.629L-403.998 404.314L-358.39 358.707ZM0.316589 0L233.938 233.622H112.637L0.316589 121.301L-112.004 233.622H-233.305L0.316589 0Z" fill="#69C0FF"/>
<path d="M-516.39 358.707L-451.914 294.23L-451.846 294.163H-330.545L-378.81 342.428L-391.272 354.889L-440.697 404.314L-434.575 410.453L-157.683 687.328L125.33 404.314L75.8879 354.889L72.4068 351.391L15.1785 294.163H136.48L136.547 294.23L187.082 344.765L246.631 404.314L-157.683 808.629L-561.998 404.314L-516.39 358.707ZM-157.683 0L75.9383 233.622H-45.3627L-157.683 121.301L-270.004 233.622H-391.305L-157.683 0Z" fill="#D9F7BE"/>
<path d="M-647.386 358.707L-582.91 294.23L-582.842 294.163H-461.541L-509.806 342.428L-522.268 354.889L-571.693 404.314L-565.571 410.453L-288.68 687.328L-5.66624 404.314L-55.1082 354.889L-58.5893 351.391L-115.818 294.163H5.48342L5.5507 294.23L56.0858 344.765L115.635 404.314L-288.68 808.629L-692.994 404.314L-647.386 358.707ZM-288.68 0L-55.0578 233.622H-176.359L-288.68 121.301L-401 233.622H-522.301L-288.68 0Z" fill="#FFF1F0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,3 @@
<svg width="16" height="13" viewBox="0 0 16 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.5 1.42863C14.9488 1.67278 14.3559 1.83568 13.7306 1.91276C14.3663 1.53529 14.8555 0.933256 15.085 0.222065C14.4901 0.570786 13.831 0.827014 13.1298 0.962003C12.5698 0.368303 11.7711 0 10.8862 0C9.18636 0 7.80856 1.36572 7.80856 3.04975C7.80856 3.28806 7.83647 3.52012 7.88897 3.74552C5.33168 3.6172 3.06354 2.40147 1.54616 0.55662C1.27952 1.00742 1.12953 1.53529 1.12953 2.09233C1.12953 3.15099 1.67157 4.08299 2.49817 4.63211C1.99363 4.6167 1.51866 4.47629 1.10287 4.25131C1.10287 4.26048 1.10287 4.27423 1.10287 4.28714C1.10287 5.7666 2.16403 6.99858 3.57058 7.27898C3.31352 7.34939 3.04187 7.38855 2.76189 7.38855C2.56316 7.38855 2.36943 7.36605 2.18194 7.33231C2.57358 8.54137 3.70973 9.42505 5.05587 9.4513C4.00262 10.2679 2.67607 10.757 1.23369 10.757C0.984543 10.757 0.740813 10.7429 0.5 10.7137C1.8628 11.5765 3.481 12.0823 5.21794 12.0823C10.8779 12.0823 13.9743 7.43438 13.9743 3.40222C13.9743 3.27014 13.9701 3.13849 13.9639 3.0085C14.568 2.58187 15.0888 2.04358 15.5 1.42863Z" fill="#03A9F4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

@@ -0,0 +1,16 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.63954 13.3644L3.95187 11.052L3.95428 11.0496H8.30453L6.5736 12.7806L6.12669 13.2275L4.35415 15L4.57368 15.2201L14.5039 25.1498L24.6537 15L22.8805 13.2275L22.7557 13.102L20.7033 11.0496H25.0535L25.0559 11.052L26.8683 12.8644L29.0039 15L14.5039 29.5L0.00390625 15L1.63954 13.3644ZM14.5039 0.5L22.8823 8.87842H18.5321L14.5039 4.85024L10.4757 8.87842H6.12548L14.5039 0.5Z" fill="#096DD9"/>
<path d="M4.35477 15.0002L6.12731 13.2276L6.57422 12.7807L4.84389 11.0498H3.9549L3.95249 11.0522L1.64016 13.3645L3.85961 15.5731L4.35477 15.0002Z" fill="url(#paint0_linear_112102_1824)"/>
<path d="M22.8814 13.2276L24.6545 15.0002L24.479 15.1757L24.4796 15.1763L26.8691 12.8646L25.0568 11.0522L25.0544 11.0498H24.8783L22.7565 13.1022L22.8814 13.2276Z" fill="url(#paint1_linear_112102_1824)"/>
<path d="M3.9497 11.0498L3.95211 11.0522L6.12693 13.2276L14.5041 21.6043L25.0586 11.0498H3.9497Z" fill="#1890FF"/>
<defs>
<linearGradient id="paint0_linear_112102_1824" x1="5.62978" y1="11.5889" x2="2.57161" y2="14.6471" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="0.9637" stop-color="#096CD9" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_112102_1824" x1="23.2722" y1="11.6281" x2="25.7451" y2="14.4382" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="1" stop-color="#096DD9" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,17 @@
<svg width="117" height="35" viewBox="0 0 117 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.63564 15.8644L6.94797 13.552L6.95038 13.5496H11.3006L9.56969 15.2806L9.12278 15.7275L7.35024 17.5L7.56977 17.7201L17.5 27.6498L27.6498 17.5L25.8766 15.7275L25.7518 15.602L23.6994 13.5496H28.0496L28.052 13.552L29.8644 15.3644L32 17.5L17.5 32L3 17.5L4.63564 15.8644ZM17.5 3L25.8784 11.3784H21.5282L17.5 7.35024L13.4718 11.3784H9.12158L17.5 3Z" fill="#096DD9"/>
<path d="M7.35037 17.5L9.12292 15.7275L9.56982 15.2805L7.8395 13.5496H6.95051L6.9481 13.552L4.63577 15.8644L6.85522 18.073L7.35037 17.5Z" fill="url(#paint0_linear_114034_8280)"/>
<path d="M25.8765 15.7275L27.6496 17.5L27.4741 17.6755L27.4747 17.6761L29.8643 15.3644L28.0519 13.552L28.0495 13.5496H27.8734L25.7516 15.602L25.8765 15.7275Z" fill="url(#paint1_linear_114034_8280)"/>
<path d="M6.9453 13.5496L6.94771 13.552L9.12253 15.7275L17.4998 24.1041L28.0542 13.5496H6.9453Z" fill="#1890FF"/>
<path d="M46.925 25.5V9.594H50.753L54.922 21.749L59.135 9.594H62.941V25.5H60.224V13.026L56.033 25.5H53.8L49.642 13.103V25.5H46.925ZM69.5711 25.72C68.8451 25.72 68.1924 25.588 67.6131 25.324C67.0411 25.0527 66.5901 24.6677 66.2601 24.169C65.9374 23.663 65.7761 23.0507 65.7761 22.332C65.7761 21.078 66.2271 20.132 67.1291 19.494C68.0384 18.856 69.4867 18.5223 71.4741 18.493L73.1681 18.449V17.679C73.1681 17.0777 72.9884 16.6083 72.6291 16.271C72.2771 15.9263 71.7271 15.7577 70.9791 15.765C70.4291 15.7723 69.9231 15.9043 69.4611 16.161C69.0064 16.4103 68.7057 16.832 68.5591 17.426H66.2821C66.3261 16.6487 66.5497 15.9997 66.9531 15.479C67.3637 14.951 67.9247 14.5587 68.6361 14.302C69.3474 14.038 70.1687 13.906 71.1001 13.906C72.1854 13.906 73.0691 14.049 73.7511 14.335C74.4331 14.621 74.9354 15.0353 75.2581 15.578C75.5807 16.1133 75.7421 16.7623 75.7421 17.525V25.5H73.4871L73.2781 23.454C72.8674 24.312 72.3467 24.906 71.7161 25.236C71.0927 25.5587 70.3777 25.72 69.5711 25.72ZM70.4401 23.905C70.7701 23.905 71.0964 23.8463 71.4191 23.729C71.7417 23.6043 72.0351 23.4393 72.2991 23.234C72.5631 23.0213 72.7721 22.7867 72.9261 22.53C73.0874 22.2733 73.1681 22.0057 73.1681 21.727V19.934L71.7711 19.956C71.0817 19.9633 70.4804 20.0367 69.9671 20.176C69.4537 20.3153 69.0504 20.539 68.7571 20.847C68.4711 21.1477 68.3281 21.551 68.3281 22.057C68.3281 22.6363 68.5297 23.091 68.9331 23.421C69.3364 23.7437 69.8387 23.905 70.4401 23.905ZM78.8735 25.5V14.126H81.4915V15.754C81.6675 15.4607 81.9058 15.1783 82.2065 14.907C82.5145 14.6357 82.8921 14.4157 83.3395 14.247C83.7868 14.0783 84.3111 13.994 84.9125 13.994C85.6165 13.994 86.2691 14.1333 86.8705 14.412C87.4791 14.6907 87.9668 15.127 88.3335 15.721C88.7075 16.315 88.8945 17.0813 88.8945 18.02V25.5H86.2105V18.339C86.2105 17.591 86.0051 17.0373 85.5945 16.678C85.1838 16.3113 84.6668 16.128 84.0435 16.128C83.6181 16.128 83.2148 16.2013 82.8335 16.348C82.4521 16.4873 82.1405 16.7037 81.8985 16.997C81.6638 17.283 81.5465 17.6423 81.5465 18.075V25.5H78.8735ZM97.652 16.018H95.111L95.122 22.541C95.122 22.8783 95.1587 23.1277 95.232 23.289C95.3127 23.443 95.4373 23.5457 95.606 23.597C95.782 23.641 96.0167 23.663 96.31 23.663H97.707V25.357C97.5603 25.4157 97.3367 25.467 97.036 25.511C96.7427 25.5623 96.3467 25.588 95.848 25.588C94.946 25.588 94.2457 25.4707 93.747 25.236C93.2557 24.994 92.9147 24.653 92.724 24.213C92.5333 23.773 92.438 23.2487 92.438 22.64V16.018H90.59V14.126H92.515L93.186 10.749H95.111V14.115H97.652V16.018ZM103.033 14.126V25.5H100.459V14.126H103.033ZM103.066 9.605V12.135H100.415V9.605H103.066ZM110.545 25.72C109.753 25.72 109.001 25.5953 108.29 25.346C107.586 25.0893 106.992 24.6897 106.508 24.147C106.031 23.6043 105.731 22.904 105.606 22.046H107.96C108.063 22.464 108.242 22.805 108.499 23.069C108.763 23.333 109.075 23.5273 109.434 23.652C109.793 23.7693 110.16 23.828 110.534 23.828C111.216 23.828 111.766 23.7107 112.184 23.476C112.609 23.2413 112.822 22.8783 112.822 22.387C112.822 22.0277 112.705 21.7417 112.47 21.529C112.235 21.3163 111.861 21.155 111.348 21.045L109.181 20.55C108.22 20.3373 107.447 19.9853 106.86 19.494C106.281 19.0027 105.987 18.3097 105.98 17.415C105.973 16.7403 106.141 16.139 106.486 15.611C106.831 15.083 107.344 14.6687 108.026 14.368C108.708 14.06 109.551 13.906 110.556 13.906C111.883 13.906 112.947 14.203 113.746 14.797C114.545 15.3837 114.956 16.2233 114.978 17.316H112.701C112.62 16.8247 112.389 16.4433 112.008 16.172C111.627 15.8933 111.132 15.754 110.523 15.754C109.892 15.754 109.375 15.875 108.972 16.117C108.569 16.359 108.367 16.7293 108.367 17.228C108.367 17.5727 108.521 17.844 108.829 18.042C109.137 18.24 109.599 18.4087 110.215 18.548L112.261 19.043C112.848 19.1897 113.328 19.3877 113.702 19.637C114.076 19.8863 114.369 20.1613 114.582 20.462C114.795 20.7553 114.941 21.0597 115.022 21.375C115.11 21.683 115.154 21.969 115.154 22.233C115.154 22.9663 114.963 23.5933 114.582 24.114C114.208 24.6273 113.676 25.0233 112.987 25.302C112.298 25.5807 111.484 25.72 110.545 25.72Z" fill="#262626"/>
<defs>
<linearGradient id="paint0_linear_114034_8280" x1="8.62538" y1="14.0888" x2="5.56721" y2="17.1469" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="0.9637" stop-color="#096CD9" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_114034_8280" x1="26.2673" y1="14.1279" x2="28.7402" y2="16.938" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="1" stop-color="#096DD9" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,16 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.63954 13.3644L3.95187 11.052L3.95428 11.0496H8.30453L6.5736 12.7806L6.12669 13.2275L4.35415 15L4.57368 15.2201L14.5039 25.1498L24.6537 15L22.8805 13.2275L22.7557 13.102L20.7033 11.0496H25.0535L25.0559 11.052L26.8683 12.8644L29.0039 15L14.5039 29.5L0.00390625 15L1.63954 13.3644ZM14.5039 0.5L22.8823 8.87842H18.5321L14.5039 4.85024L10.4757 8.87842H6.12548L14.5039 0.5Z" fill="currentColor"/>
<path d="M4.35477 15.0002L6.12731 13.2276L6.57422 12.7807L4.84389 11.0498H3.9549L3.95249 11.0522L1.64016 13.3645L3.85961 15.5731L4.35477 15.0002Z" fill="url(#paint0_linear_112102_1824)"/>
<path d="M22.8814 13.2276L24.6545 15.0002L24.479 15.1757L24.4796 15.1763L26.8691 12.8646L25.0568 11.0522L25.0544 11.0498H24.8783L22.7565 13.1022L22.8814 13.2276Z" fill="url(#paint1_linear_112102_1824)"/>
<path d="M3.9497 11.0498L3.95211 11.0522L6.12693 13.2276L14.5041 21.6043L25.0586 11.0498H3.9497Z" fill="#1890FF"/>
<defs>
<linearGradient id="paint0_linear_112102_1824" x1="5.62978" y1="11.5889" x2="2.57161" y2="14.6471" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="0.9637" stop-color="#096CD9" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_112102_1824" x1="23.2722" y1="11.6281" x2="25.7451" y2="14.4382" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="1" stop-color="#096DD9" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,17 @@
<svg width="118" height="35" viewBox="0 0 118 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.44814 15.8644L7.76047 13.552L7.76288 13.5496H12.1131L10.3822 15.2806L9.93528 15.7275L8.16274 17.5L8.38227 17.7201L18.3125 27.6498L28.4623 17.5L26.6891 15.7275L26.5643 15.602L24.5119 13.5496H28.8621L28.8645 13.552L30.6769 15.3644L32.8125 17.5L18.3125 32L3.8125 17.5L5.44814 15.8644ZM18.3125 3L26.6909 11.3784H22.3407L18.3125 7.35024L14.2843 11.3784H9.93408L18.3125 3Z" fill="#096DD9"/>
<path d="M8.16287 17.5002L9.93542 15.7276L10.3823 15.2807L8.652 13.5498H7.76301L7.7606 13.5522L5.44827 15.8645L7.66772 18.0731L8.16287 17.5002Z" fill="url(#paint0_linear_113047_293987)"/>
<path d="M26.6892 15.7276L28.4624 17.5002L28.2869 17.6757L28.2875 17.6763L30.677 15.3646L28.8647 13.5522L28.8622 13.5498H28.6861L26.5644 15.6022L26.6892 15.7276Z" fill="url(#paint1_linear_113047_293987)"/>
<path d="M7.75805 13.5498L7.76046 13.5522L9.93528 15.7276L18.3125 24.1043L28.8669 13.5498H7.75805Z" fill="#1890FF"/>
<path d="M47.7375 25.5V9.594H51.5655L55.7345 21.749L59.9475 9.594H63.7535V25.5H61.0365V13.026L56.8455 25.5H54.6125L50.4545 13.103V25.5H47.7375ZM70.3836 25.72C69.6576 25.72 69.0049 25.588 68.4256 25.324C67.8536 25.0527 67.4026 24.6677 67.0726 24.169C66.7499 23.663 66.5886 23.0507 66.5886 22.332C66.5886 21.078 67.0396 20.132 67.9416 19.494C68.8509 18.856 70.2992 18.5223 72.2866 18.493L73.9806 18.449V17.679C73.9806 17.0777 73.8009 16.6083 73.4416 16.271C73.0896 15.9263 72.5396 15.7577 71.7916 15.765C71.2416 15.7723 70.7356 15.9043 70.2736 16.161C69.8189 16.4103 69.5182 16.832 69.3716 17.426H67.0946C67.1386 16.6487 67.3622 15.9997 67.7656 15.479C68.1762 14.951 68.7372 14.5587 69.4486 14.302C70.1599 14.038 70.9812 13.906 71.9126 13.906C72.9979 13.906 73.8816 14.049 74.5636 14.335C75.2456 14.621 75.7479 15.0353 76.0706 15.578C76.3932 16.1133 76.5546 16.7623 76.5546 17.525V25.5H74.2996L74.0906 23.454C73.6799 24.312 73.1592 24.906 72.5286 25.236C71.9052 25.5587 71.1902 25.72 70.3836 25.72ZM71.2526 23.905C71.5826 23.905 71.9089 23.8463 72.2316 23.729C72.5542 23.6043 72.8476 23.4393 73.1116 23.234C73.3756 23.0213 73.5846 22.7867 73.7386 22.53C73.8999 22.2733 73.9806 22.0057 73.9806 21.727V19.934L72.5836 19.956C71.8942 19.9633 71.2929 20.0367 70.7796 20.176C70.2662 20.3153 69.8629 20.539 69.5696 20.847C69.2836 21.1477 69.1406 21.551 69.1406 22.057C69.1406 22.6363 69.3422 23.091 69.7456 23.421C70.1489 23.7437 70.6512 23.905 71.2526 23.905ZM79.686 25.5V14.126H82.304V15.754C82.48 15.4607 82.7183 15.1783 83.019 14.907C83.327 14.6357 83.7046 14.4157 84.152 14.247C84.5993 14.0783 85.1236 13.994 85.725 13.994C86.429 13.994 87.0816 14.1333 87.683 14.412C88.2916 14.6907 88.7793 15.127 89.146 15.721C89.52 16.315 89.707 17.0813 89.707 18.02V25.5H87.023V18.339C87.023 17.591 86.8176 17.0373 86.407 16.678C85.9963 16.3113 85.4793 16.128 84.856 16.128C84.4306 16.128 84.0273 16.2013 83.646 16.348C83.2646 16.4873 82.953 16.7037 82.711 16.997C82.4763 17.283 82.359 17.6423 82.359 18.075V25.5H79.686ZM98.4645 16.018H95.9235L95.9345 22.541C95.9345 22.8783 95.9712 23.1277 96.0445 23.289C96.1252 23.443 96.2498 23.5457 96.4185 23.597C96.5945 23.641 96.8292 23.663 97.1225 23.663H98.5195V25.357C98.3728 25.4157 98.1492 25.467 97.8485 25.511C97.5552 25.5623 97.1592 25.588 96.6605 25.588C95.7585 25.588 95.0582 25.4707 94.5595 25.236C94.0682 24.994 93.7272 24.653 93.5365 24.213C93.3458 23.773 93.2505 23.2487 93.2505 22.64V16.018H91.4025V14.126H93.3275L93.9985 10.749H95.9235V14.115H98.4645V16.018ZM103.845 14.126V25.5H101.271V14.126H103.845ZM103.878 9.605V12.135H101.227V9.605H103.878ZM111.358 25.72C110.566 25.72 109.814 25.5953 109.103 25.346C108.399 25.0893 107.805 24.6897 107.321 24.147C106.844 23.6043 106.543 22.904 106.419 22.046H108.773C108.875 22.464 109.055 22.805 109.312 23.069C109.576 23.333 109.887 23.5273 110.247 23.652C110.606 23.7693 110.973 23.828 111.347 23.828C112.029 23.828 112.579 23.7107 112.997 23.476C113.422 23.2413 113.635 22.8783 113.635 22.387C113.635 22.0277 113.517 21.7417 113.283 21.529C113.048 21.3163 112.674 21.155 112.161 21.045L109.994 20.55C109.033 20.3373 108.259 19.9853 107.673 19.494C107.093 19.0027 106.8 18.3097 106.793 17.415C106.785 16.7403 106.954 16.139 107.299 15.611C107.643 15.083 108.157 14.6687 108.839 14.368C109.521 14.06 110.364 13.906 111.369 13.906C112.696 13.906 113.759 14.203 114.559 14.797C115.358 15.3837 115.769 16.2233 115.791 17.316H113.514C113.433 16.8247 113.202 16.4433 112.821 16.172C112.439 15.8933 111.944 15.754 111.336 15.754C110.705 15.754 110.188 15.875 109.785 16.117C109.381 16.359 109.18 16.7293 109.18 17.228C109.18 17.5727 109.334 17.844 109.642 18.042C109.95 18.24 110.412 18.4087 111.028 18.548L113.074 19.043C113.66 19.1897 114.141 19.3877 114.515 19.637C114.889 19.8863 115.182 20.1613 115.395 20.462C115.607 20.7553 115.754 21.0597 115.835 21.375C115.923 21.683 115.967 21.969 115.967 22.233C115.967 22.9663 115.776 23.5933 115.395 24.114C115.021 24.6273 114.489 25.0233 113.8 25.302C113.11 25.5807 112.296 25.72 111.358 25.72Z" fill="#F0F0F0"/>
<defs>
<linearGradient id="paint0_linear_113047_293987" x1="9.43788" y1="14.0889" x2="6.37971" y2="17.1471" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="0.9637" stop-color="#096CD9" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_113047_293987" x1="27.08" y1="14.1281" x2="29.5529" y2="16.9382" gradientUnits="userSpaceOnUse">
<stop stop-color="#023B95"/>
<stop offset="1" stop-color="#096DD9" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,3 @@
<svg width="333" height="61" viewBox="0 0 333 61" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M-0.322477 0.641574L-0.418408 0.552128L-9.20939 59.4302L23.6588 106.207L154.575 130.424C236.759 117.931 383.93 93.3331 315.142 94.8795C246.355 96.4258 215.362 64.279 215.362 64.279C215.362 64.279 185.497 26.9121 117.864 33.4284C42.6115 40.6788 10.6143 10.8404 -0.322477 0.641574Z" fill="#BAE7FF"/>
</svg>

After

Width:  |  Height:  |  Size: 426 B

@@ -0,0 +1,3 @@
<svg width="447" height="116" viewBox="0 0 447 116" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M55.2678 22.378C-49.5465 -14.1608 7.16534 -48.8526 136.242 -34.0644L214.579 -30.0721L448.26 -8.82548L459.956 104.858C396.401 148.387 406.862 51.7169 297.501 67.1295C188.139 82.5422 225.278 33.3223 176.928 20.0909C128.579 6.8595 91.4243 34.9824 55.2678 22.378Z" fill="#BAE7FF"/>
</svg>

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

+2 -4
View File
@@ -5,13 +5,11 @@
<title>JambotronUi</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="Firefly_jambo-tron-118290.ico">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(App, config);
export default bootstrap;
+4 -13
View File
@@ -1,15 +1,6 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { appConfig } from './app/app.config';
import { App } from './app/app';
import {AppModule} from './app/app.module';
//import('@angular/animations/browser')
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(
// err => console.error(err)
(e) => {
console.error(e.message);
}
);
bootstrapApplication(App, appConfig)
.catch((err) => console.error(err));
+34
View File
@@ -0,0 +1,34 @@
@import '../../../node_modules/bootstrap/scss/maps';
@import '../../../node_modules/bootstrap/scss/mixins';
@import '../../../node_modules/bootstrap/scss/root';
@import '../../../node_modules/bootstrap/scss/reboot';
@import '../../../node_modules/bootstrap/scss/type';
@import '../../../node_modules/bootstrap/scss/images';
@import '../../../node_modules/bootstrap/scss/containers';
@import '../../../node_modules/bootstrap/scss/grid';
@import '../../../node_modules/bootstrap/scss/tables';
@import '../../../node_modules/bootstrap/scss/forms';
@import '../../../node_modules/bootstrap/scss/buttons';
@import '../../../node_modules/bootstrap/scss/transitions';
@import '../../../node_modules/bootstrap/scss/dropdown';
@import '../../../node_modules/bootstrap/scss/button-group';
@import '../../../node_modules/bootstrap/scss/nav';
@import '../../../node_modules/bootstrap/scss/navbar';
@import '../../../node_modules/bootstrap/scss/card';
@import '../../../node_modules/bootstrap/scss/accordion';
@import '../../../node_modules/bootstrap/scss/breadcrumb';
@import '../../../node_modules/bootstrap/scss/pagination';
@import '../../../node_modules/bootstrap/scss/badge';
@import '../../../node_modules/bootstrap/scss/alert';
@import '../../../node_modules/bootstrap/scss/progress';
@import '../../../node_modules/bootstrap/scss/list-group';
@import '../../../node_modules/bootstrap/scss/close';
@import '../../../node_modules/bootstrap/scss/toasts';
@import '../../../node_modules/bootstrap/scss/modal';
@import '../../../node_modules/bootstrap/scss/tooltip';
@import '../../../node_modules/bootstrap/scss/popover';
@import '../../../node_modules/bootstrap/scss/carousel';
@import '../../../node_modules/bootstrap/scss/spinners';
@import '../../../node_modules/bootstrap/scss/offcanvas';
@import '../../../node_modules/bootstrap/scss/placeholders';
@import '../../../node_modules/bootstrap/scss/helpers';
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,39 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src:
url('Inter-Regular.woff2?v=3.13') format('woff2'),
url('Inter-Regular.woff?v=3.13') format('woff');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src:
url('Inter-Medium.woff2?v=3.13') format('woff2'),
url('Inter-Medium.woff?v=3.13') format('woff');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src:
url('Inter-SemiBold.woff2?v=3.13') format('woff2'),
url('Inter-SemiBold.woff?v=3.13') format('woff');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src:
url('Inter-Bold.woff2?v=3.13') format('woff2'),
url('Inter-Bold.woff?v=3.13') format('woff');
}
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.
Binary file not shown.
+735
View File
@@ -0,0 +1,735 @@
// Variables
//
// Variables should follow the `$component-state-property-size` formula for
// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
// Color system
$grays: (
'100': $gray-100,
'200': $gray-200,
'300': $gray-300,
'400': $gray-400,
'500': $gray-500,
'600': $gray-600,
'700': $gray-700,
'800': $gray-800,
'900': $gray-900
);
// scss-docs-start colors-map
$colors: (
'blue': $blue,
'indigo': $indigo,
'purple': $purple,
'pink': $pink,
'red': $red,
'orange': $orange,
'yellow': $yellow,
'green': $green,
'teal': $teal,
'cyan': $cyan,
'black': $black,
'white': $white,
'gray': $gray-600,
'gray-dark': $gray-800
);
// scss-docs-end colors-map
$primary: $blue; // change
$secondary: $secondary; // change
$success: $green; // change
$info: $cyan; // change
$warning: $yellow; // change
$danger: $red; // change
$light: $gray-100; // change
$dark: $dark; // change
// scss-docs-start theme-colors-map
$theme-colors: (
'primary': $primary,
'secondary': $secondary,
'success': $success,
'info': $info,
'warning': $warning,
'danger': $danger,
'light': $light,
'dark': $dark
);
// scss-docs-end theme-colors-map
// scss-docs-start theme-colors-rgb
$theme-colors-rgb: map-loop($theme-colors, to-rgb, '$value');
// scss-docs-end theme-colors-rgb
// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.
// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
$min-contrast-ratio: 1.55;
// Customize the light and dark text colors for use in our color contrast function.
$color-contrast-dark: $body-color;
$color-contrast-light: $white;
// fusv-disable
$blues: (
'blue-100': $blue-100,
'blue-200': $blue-200,
'blue-300': $blue-300,
'blue-400': $blue-400,
'blue-500': $blue-500,
'blue-600': $blue-600,
'blue-700': $blue-700,
'blue-800': $blue-800,
'blue-900': $blue-900
);
// Characters which are escaped by the escape-svg function
$escaped-characters: (('<', '%3c'), ('>', '%3e'), ('#', '%23'), ('(', '%28'), (')', '%29'));
$variable-prefix: bs-; // Deprecated in v5.2.0 for the shorter `$prefix`
$prefix: $variable-prefix;
// Gradient
$gradient: linear-gradient(180deg, rgba($white, 0.15), rgba($white, 0));
// Spacing
$spacer: 1rem;
$spacers: (
0: 0,
1: $spacer * 0.25,
2: $spacer * 0.5,
3: $spacer,
4: $spacer * 1.5,
5: $spacer * 3
);
// scss-docs-end spacer-variables-maps
// Position
//
// Define the edge positioning anchors of the position utilities.
// scss-docs-start position-map
$position-values: (
0: 0,
50: 50%,
100: 100%
);
// scss-docs-end position-map
// Body
//
// Settings for the `<body>` element.
$body-bg: #fafafb; // change
$body-color: $gray-900; // change
$body-text-align: null;
// Links
//
// Style anchor elements.
$link-color: $primary;
$link-decoration: none;
$link-shade-percentage: 20%;
$link-hover-color: shift-color($link-color, $link-shade-percentage);
$link-hover-decoration: underline;
$stretched-link-pseudo-element: after;
$stretched-link-z-index: 1;
// Paragraphs
//
// Style p element.
$paragraph-margin-bottom: 1rem;
// Grid breakpoints
//
// Define the minimum dimensions at which your layout will change,
// adapting to different screen sizes, for use in media queries.
// scss-docs-start grid-breakpoints
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// scss-docs-end grid-breakpoints
@include _assert-ascending($grid-breakpoints, '$grid-breakpoints');
@include _assert-starts-at-zero($grid-breakpoints, '$grid-breakpoints');
// Grid containers
//
// Define the maximum width of `.container` for different screen sizes.
// scss-docs-start container-max-widths
$container-max-widths: (
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px,
xxl: 1320px
);
// scss-docs-end container-max-widths
@include _assert-ascending($container-max-widths, '$container-max-widths');
// Grid columns
//
// Set the number of columns and specify the width of the gutters.
$grid-columns: 12;
$grid-gutter-width: 1.5rem;
$grid-row-columns: 6;
// Container padding
$container-padding-x: $grid-gutter-width;
// Components
//
// Define common padding and border radius sizes and more.
// scss-docs-start border-variables
$border-width: 1px;
$border-widths: (
0: 0,
1: 1px,
2: 2px,
3: 3px,
4: 4px,
5: 5px
);
$border-style: solid;
$border-color: #e6ebf1;
$border-color-translucent: rgba($black, 0.175);
$border-radius: 4px; // change
$border-radius-lg: 6px; // change
$border-radius-sm: 2px; // change
$border-radius-pill: 50rem;
// scss-docs-end border-radius-variables
$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
$box-shadow-lg: 0 1rem 3rem rgba($black, 0.175);
$box-shadow-inset: inset 0 1px 2px rgba($black, 0.075);
$component-active-color: $white;
$component-active-bg: var(--bs-primary);
// scss-docs-start caret-variables
$caret-width: 0.3em;
$caret-vertical-align: $caret-width * 0.85;
$caret-spacing: $caret-width * 0.85;
// scss-docs-end caret-variables
$transition-base: all 0.2s ease-in-out;
$transition-fade: opacity 0.15s linear;
// scss-docs-start collapse-transition
$transition-collapse: height 0.35s ease;
$transition-collapse-width: width 0.35s ease;
// stylelint-disable function-disallowed-list
// scss-docs-start aspect-ratios
$aspect-ratios: (
'1x1': 100%,
'4x3': calc(3 / 4 * 100%),
'16x9': calc(9 / 16 * 100%),
'21x9': calc(9 / 21 * 100%)
);
// scss-docs-end aspect-ratios
// stylelint-enable function-disallowed-list
// Typography
//
// Font, line-height, and color for body text, headings, and more.
// stylelint-disable value-keyword-case
$font-family-sans-serif: 'Public Sans', sans-serif; // change
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
// stylelint-enable value-keyword-case
$font-family-base: var(--#{$variable-prefix}font-sans-serif);
$font-family-code: var(--#{$variable-prefix}font-monospace);
$font-size-root: null;
$font-size-base: 0.875rem;
$font-size-sm: $font-size-base * 0.875;
$font-size-lg: $font-size-base * 1.25;
$font-weight-lighter: lighter;
$font-weight-light: 300;
$font-weight-normal: 400;
$font-weight-bold: 700;
$font-weight-bolder: bolder;
$font-weight-base: $font-weight-normal;
$line-height-base: 1.5;
$line-height-sm: 1.25;
$line-height-lg: 2;
$h1-font-size: 40px; // change
$h2-font-size: 30px; // change
$h3-font-size: 24px; // change
$h4-font-size: 20px; // change
$h5-font-size: 16px; // change
$h6-font-size: 14px; // change
// scss-docs-start font-sizes
$font-sizes: (
1: $h1-font-size,
2: $h2-font-size,
3: $h3-font-size,
4: $h4-font-size,
5: $h5-font-size,
6: $h6-font-size
);
// scss-docs-end font-sizes
// scss-docs-end font-sizes
h1,
h2 {
font-weight: 700;
}
$headings-margin-bottom: calc($spacer / 2);
$headings-font-family: null;
$headings-font-style: null;
$headings-font-weight: 600;
$headings-line-height: 1.2;
$headings-color: #262626;
$label-color: $gray-900;
// scss-docs-start display-headings
$display-font-sizes: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem
);
$display-font-weight: 300;
$display-line-height: $headings-line-height;
// scss-docs-end display-headings
$lead-font-size: $font-size-base * 1.25;
$lead-font-weight: 300;
$small-font-size: 80%;
$sub-sup-font-size: 0.75em;
$text-muted: $gray-600;
$hr-margin-y: $spacer;
$hr-color: inherit;
$hr-height: $border-width;
$hr-opacity: 0.13;
$legend-margin-bottom: 0.5rem;
$legend-font-size: 1.5rem;
$legend-font-weight: null;
$mark-padding: 0.2em;
$dt-font-weight: $font-weight-bold;
$nested-kbd-font-weight: $font-weight-bold;
$list-inline-padding: 0.5rem;
$mark-bg: #fcf8e3;
// Tables
//
// Customizes the `.table` component with basic values, each used across all table variations.
// scss-docs-start table-variables
$table-cell-padding-y: 0.9rem;
$table-cell-padding-x: 0.75rem;
$table-cell-padding-y-sm: 0.3rem;
$table-cell-padding-x-sm: 0.3rem;
$table-cell-vertical-align: top;
$table-color: $body-color;
$table-bg: transparent;
$table-accent-bg: transparent;
$table-th-font-weight: null;
$table-striped-color: $table-color;
$table-striped-bg-factor: 0.05;
$table-striped-bg: rgba($black, $table-striped-bg-factor);
$table-active-color: $table-color;
$table-active-bg-factor: 0.1;
$table-active-bg: rgba($black, $table-active-bg-factor);
$table-hover-color: $table-color;
$table-hover-bg-factor: 0.02;
$table-hover-bg: rgba($primary, $table-hover-bg-factor);
$table-border-factor: 0.1;
$table-border-width: $border-width;
$table-border-color: $border-color;
$table-striped-order: odd;
$table-group-seperator-color: currentColor;
$table-caption-color: $text-muted;
$table-bg-scale: -80%;
$table-variants: (
'primary': shift-color($primary, $table-bg-scale),
'secondary': shift-color($secondary, $table-bg-scale),
'success': shift-color($success, $table-bg-scale),
'info': shift-color($info, $table-bg-scale),
'warning': shift-color($warning, $table-bg-scale),
'danger': shift-color($danger, $table-bg-scale),
'light': $light,
'dark': $dark
);
// scss-docs-end table-variables
// Buttons + Forms
//
// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
$input-btn-padding-y: 0.407rem;
$input-btn-padding-x: 1rem;
$input-btn-font-family: null;
$input-btn-font-size: 0.875rem;
$input-btn-line-height: $line-height-base;
$input-btn-focus-width: 0.2rem;
$input-btn-focus-color-opacity: 0.25;
$input-btn-focus-color: rgba($component-active-bg, $input-btn-focus-color-opacity);
$input-btn-focus-blur: 0;
$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color;
$input-btn-padding-y-sm: 0.25rem;
$input-btn-padding-x-sm: 0.5rem;
$input-btn-font-size-sm: $font-size-sm;
$input-btn-padding-y-lg: 1rem;
$input-btn-padding-x-lg: 1.3rem;
$input-btn-font-size-lg: $font-size-lg;
$input-btn-border-width: 1px;
// Buttons
//
// For each of Bootstrap's buttons, define text, background, and border color.
$btn-padding-y: $input-btn-padding-y;
$btn-padding-x: $input-btn-padding-x;
$btn-font-family: $input-btn-font-family;
$btn-font-size: $input-btn-font-size;
$btn-line-height: $input-btn-line-height;
$btn-white-space: null; // Set to `nowrap` to prevent text wrapping
$btn-padding-y-sm: $input-btn-padding-y-sm;
$btn-padding-x-sm: $input-btn-padding-x-sm;
$btn-font-size-sm: $input-btn-font-size-sm;
$btn-padding-y-lg: $input-btn-padding-y-lg;
$btn-padding-x-lg: $input-btn-padding-x-lg;
$btn-font-size-lg: $input-btn-font-size-lg;
$btn-border-width: $input-btn-border-width;
$btn-font-weight: 400;
$btn-box-shadow:
inset 0 1px 0 rgba($white, 0.15),
0 1px 1px rgba($black, 0.075);
$btn-focus-width: $input-btn-focus-width;
$btn-focus-box-shadow: $input-btn-focus-box-shadow;
$btn-disabled-opacity: 0.65;
$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125);
$btn-link-color: $link-color;
$btn-link-hover-color: $link-hover-color;
$btn-link-disabled-color: $gray-600;
// Allows for customizing button radius independently from global border radius
$btn-border-radius: 4px;
$btn-border-radius-sm: 2px;
$btn-border-radius-lg: 6px;
$btn-transition:
color 0.15s ease-in-out,
background-color 0.15s ease-in-out,
border-color 0.15s ease-in-out,
box-shadow 0.15s ease-in-out;
$btn-hover-bg-shade-amount: 15%;
$btn-hover-bg-tint-amount: 15%;
$btn-hover-border-shade-amount: 20%;
$btn-hover-border-tint-amount: 10%;
$btn-active-bg-shade-amount: 20%;
$btn-active-bg-tint-amount: 20%;
$btn-active-border-shade-amount: 25%;
$btn-active-border-tint-amount: 10%;
// scss-docs-end btn-variables
// Forms
$form-text-margin-top: 0.25rem;
$form-text-font-size: $small-font-size;
$form-text-font-style: null;
$form-text-font-weight: null;
$form-text-color: $text-muted;
$form-label-margin-bottom: 0.5rem;
$form-label-font-size: null;
$form-label-font-style: null;
$form-label-font-weight: null;
$form-label-color: $label-color;
$input-padding-y: 0.65rem;
$input-padding-x: 0.75rem;
$input-font-family: $input-btn-font-family;
$input-font-size: $input-btn-font-size;
$input-font-weight: $font-weight-base;
$input-line-height: $input-btn-line-height;
$input-padding-y-sm: 0.375rem;
$input-padding-x-sm: 0.7rem;
$input-font-size-sm: $input-btn-font-size-sm;
$input-padding-y-lg: 0.775rem;
$input-padding-x-lg: 0.85rem;
$input-font-size-lg: $input-btn-font-size-lg;
$input-bg: $white;
$input-disabled-bg: $gray-200;
$input-disabled-border-color: null;
$input-color: $body-color;
$input-border-color: $gray-400;
$input-border-width: 1px;
$input-box-shadow: inset 0 1px 1px rgba($black, 0.075);
$input-border-radius: 4px;
$input-border-radius-sm: 2px;
$input-border-radius-lg: 6px;
$input-focus-bg: $input-bg;
$input-focus-border-color: $primary;
$input-focus-color: $input-color;
$input-focus-width: $input-btn-focus-width;
$input-focus-box-shadow: 0 0 0 2px rgba($component-active-bg, 0.2);
$input-placeholder-color: $gray-600;
$input-plaintext-color: $headings-color;
$input-height-border: $input-border-width * 2;
$input-height-inner: add($input-line-height * 1em, calc($input-padding-y * 2));
$input-height-inner-half: add($input-line-height * 0.5em, $input-padding-y);
$input-height-inner-quarter: add($input-line-height * 0.25em, calc($input-padding-y / 2));
$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false));
$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false));
$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false));
$input-transition:
border-color 0.15s ease-in-out,
box-shadow 0.15s ease-in-out;
$form-color-width: 3rem;
// scss-docs-end form-input-variables
// scss-docs-end form-validation-states
// Z-index master list
$zindex-dropdown: 1026;
$zindex-sticky: 1020;
$zindex-fixed: 1030;
// Navs
$nav-link-padding-y: 0.5rem;
$nav-link-padding-x: 1rem;
$nav-link-font-size: null;
$nav-link-font-weight: null;
$nav-link-color: null;
$nav-link-hover-color: null;
$nav-link-transition:
color 0.15s ease-in-out,
background-color 0.15s ease-in-out,
border-color 0.15s ease-in-out;
$nav-link-disabled-color: $gray-600;
$nav-tabs-border-color: $gray-300;
$nav-tabs-border-width: $border-width;
$nav-tabs-border-radius: $border-radius;
$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color;
$nav-tabs-link-active-color: $gray-700;
$nav-tabs-link-active-bg: $white; // change
$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg;
$nav-pills-border-radius: $border-radius;
$nav-pills-link-active-color: $component-active-color;
$nav-pills-link-active-bg: $component-active-bg;
// Navbar
$navbar-padding-y: calc(#{$spacer} / 2);
$navbar-padding-x: null;
$navbar-nav-link-padding-x: 0.5rem;
$navbar-brand-font-size: $font-size-lg;
// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2;
$navbar-brand-height: $navbar-brand-font-size * $line-height-base;
$navbar-brand-padding-y: calc(($nav-link-height - $navbar-brand-height) / 2);
$navbar-brand-margin-end: 1rem;
$navbar-toggler-padding-y: 0.25rem;
$navbar-toggler-padding-x: 0.75rem;
$navbar-toggler-font-size: $font-size-lg;
$navbar-toggler-border-radius: $btn-border-radius;
$navbar-toggler-focus-width: $btn-focus-width;
$navbar-toggler-transition: box-shadow 0.15s ease-in-out;
$navbar-dark-color: rgba($white, 0.9);
$navbar-dark-hover-color: rgba($white, 0.75);
$navbar-dark-active-color: $white;
$navbar-dark-disabled-color: rgba($white, 0.25);
$navbar-dark-toggler-border-color: rgba($white, 0.1);
$navbar-light-color: rgba($black, 0.55);
$navbar-light-hover-color: rgba($black, 0.7);
$navbar-light-active-color: rgba($black, 0.9);
$navbar-light-disabled-color: rgba($black, 0.3);
$navbar-light-toggler-border-color: rgba($black, 0.1);
$navbar-light-brand-color: $navbar-light-active-color;
$navbar-light-brand-hover-color: $navbar-light-active-color;
$navbar-dark-brand-color: $navbar-dark-active-color;
$navbar-dark-brand-hover-color: $navbar-dark-active-color;
// Dropdowns
$dropdown-font-size: $font-size-base;
$dropdown-color: $body-color;
$dropdown-bg: $white;
// scss-docs-start placeholders
$placeholder-opacity-max: 0.5;
$placeholder-opacity-min: 0.2;
// scss-docs-end placeholders
// Cards
$card-spacer-y: 25px; // change
$card-spacer-x: 25px; // change
$card-title-spacer-y: calc($spacer / 2);
$card-border-width: 1px; // change
$card-border-radius: $border-radius;
$card-border-color: $border-color;
$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width});
$card-cap-padding-y: 25px;
$card-cap-padding-x: 25px;
$card-cap-bg: transparent;
$card-cap-color: null;
$card-height: null;
$card-color: null;
$card-bg: $white;
$card-img-overlay-padding: 1.25rem;
$card-group-margin: calc($grid-gutter-width / 2);
// Badges
$badge-font-size: 0.75em;
$badge-font-weight: 500;
$badge-color: $white;
$badge-padding-y: 0.35em;
$badge-padding-x: 0.5em;
$badge-border-radius: 2px;
// List group
$list-group-color: null;
$list-group-bg: $white;
$list-group-border-color: $border-color;
$list-group-border-width: $border-width;
$list-group-border-radius: $border-radius;
$list-group-item-padding-y: calc($card-spacer-y / 1.5);
$list-group-item-padding-x: $card-spacer-x;
$list-group-item-bg-scale: -80%;
$list-group-item-color-scale: 40%;
$list-group-hover-bg: $gray-100;
$list-group-active-color: $component-active-color;
$list-group-active-bg: $component-active-bg;
$list-group-active-border-color: $list-group-active-bg;
$list-group-disabled-color: $gray-600;
$list-group-disabled-bg: $list-group-bg;
$list-group-action-color: $gray-700;
$list-group-action-hover-color: $list-group-action-color;
$list-group-action-active-color: $body-color;
$list-group-action-active-bg: $gray-200;
// Figures
$figure-caption-font-size: 90%;
$figure-caption-color: $gray-600;
// Breadcrumbs
$breadcrumb-font-size: null;
$breadcrumb-padding-y: 2;
$breadcrumb-padding-x: 0;
$breadcrumb-item-padding: 0.5rem;
$breadcrumb-margin-bottom: 1rem;
$breadcrumb-bg: null;
$breadcrumb-divider-color: $gray-600;
$breadcrumb-active-color: $gray-600;
$breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='14' height='14' stroke='#{$gray-600}' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round' class='css-i6dzq1'%3E%3Cpolyline points='9 18 15 12 9 6'%3E%3C/polyline%3E%3C/svg%3E");
$breadcrumb-divider-flipped: $breadcrumb-divider;
$breadcrumb-border-radius: null;
// Close
$btn-close-width: 1em;
$btn-close-height: $btn-close-width;
$btn-close-padding-x: 0.25em;
$btn-close-padding-y: $btn-close-padding-x;
$btn-close-color: $black;
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#{$btn-close-color}' viewBox='0 0 16 16'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
$btn-close-focus-shadow: $input-btn-focus-box-shadow;
$btn-close-opacity: 0.5;
$btn-close-hover-opacity: 0.75;
$btn-close-focus-opacity: 1;
$btn-close-disabled-opacity: 0.25;
$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
// Code
$code-font-size: 87.5%;
$code-color: $pink;
$kbd-padding-y: 0.2rem;
$kbd-padding-x: 0.4rem;
$kbd-font-size: $code-font-size;
$kbd-color: $white;
$kbd-bg: $body-color;
$pre-color: null;
// scss-docs-start theme-colors-rgb
$theme-colors-rgb: map-loop($theme-colors, to-rgb, '$value');
@@ -0,0 +1,44 @@
// =======================================
// List of variables for Preset color
// =======================================
// Gray color
$white: #ffffff;
$gray-100: #fafafa;
$gray-200: #f5f5f5;
$gray-300: #f0f0f0;
$gray-400: #d9d9d9;
$gray-500: #bfbfbf;
$gray-600: #8c8c8c;
$gray-700: #595959;
$gray-800: #262626;
$gray-900: #141414;
$black: #000000;
$blue: #1677ff;
$indigo: #6610f2;
$purple: #6f42c1;
$pink: #e83e8c;
$red: #ff4d4f;
$orange: #fd7e14;
$yellow: #faad14;
$green: #52c41a;
$teal: #20c997;
$cyan: #13c2c2;
$blue-100: tint-color($blue, 80%);
$blue-200: tint-color($blue, 60%);
$blue-300: tint-color($blue, 40%);
$blue-400: tint-color($blue, 20%);
$blue-500: $blue;
$blue-600: shade-color($blue, 20%);
$blue-700: shade-color($blue, 40%);
$blue-800: shade-color($blue, 60%);
$blue-900: shade-color($blue, 80%);
$preset-colors: (
preset-1: (
primary: #1890ff
)
);
$dark-bg-color: #1a1a1a;

Some files were not shown because too many files have changed in this diff Show More