├── src ├── assets │ ├── demo │ │ ├── demo.scss │ │ ├── flags │ │ │ ├── flags_responsive.png │ │ │ └── flags.scss │ │ └── code.scss │ ├── layout │ │ ├── variables │ │ │ ├── _light.scss │ │ │ ├── _dark.scss │ │ │ └── _common.scss │ │ ├── _footer.scss │ │ ├── layout.scss │ │ ├── _main.scss │ │ ├── _core.scss │ │ ├── _utils.scss │ │ ├── _mixins.scss │ │ ├── _preloading.scss │ │ ├── _typography.scss │ │ ├── _responsive.scss │ │ ├── _topbar.scss │ │ └── _menu.scss │ ├── styles.scss │ └── tailwind.css ├── main.ts ├── app.component.ts ├── app │ ├── pages │ │ ├── auth │ │ │ ├── auth.routes.ts │ │ │ ├── error.ts │ │ │ ├── access.ts │ │ │ └── login.ts │ │ ├── empty │ │ │ └── empty.ts │ │ ├── pages.routes.ts │ │ ├── service │ │ │ ├── icon.service.ts │ │ │ └── photo.service.ts │ │ ├── dashboard │ │ │ ├── dashboard.ts │ │ │ └── components │ │ │ │ ├── recentsaleswidget.ts │ │ │ │ ├── statswidget.ts │ │ │ │ ├── revenuestreamwidget.ts │ │ │ │ ├── notificationswidget.ts │ │ │ │ └── bestsellingwidget.ts │ │ ├── landing │ │ │ ├── landing.ts │ │ │ └── components │ │ │ │ ├── herowidget.ts │ │ │ │ ├── highlightswidget.ts │ │ │ │ ├── pricingwidget.ts │ │ │ │ ├── topbarwidget.component.ts │ │ │ │ ├── footerwidget.ts │ │ │ │ └── featureswidget.ts │ │ ├── uikit │ │ │ ├── uikit.routes.ts │ │ │ ├── filedemo.ts │ │ │ ├── treedemo.ts │ │ │ ├── messagesdemo.ts │ │ │ ├── mediademo.ts │ │ │ ├── timelinedemo.ts │ │ │ └── formlayoutdemo.ts │ │ ├── documentation │ │ │ └── documentation.ts │ │ └── notfound │ │ │ └── notfound.ts │ └── layout │ │ ├── component │ │ ├── app.footer.ts │ │ ├── app.sidebar.ts │ │ ├── app.floatingconfigurator.ts │ │ ├── app.layout.ts │ │ ├── app.menuitem.ts │ │ ├── app.menu.ts │ │ └── app.topbar.ts │ │ └── service │ │ └── layout.service.ts ├── index.html ├── app.config.ts └── app.routes.ts ├── .postcssrc.json ├── vercel.json ├── .prettierignore ├── .editorconfig ├── tsconfig.app.json ├── tsconfig.spec.json ├── .prettierrc.json ├── .gitignore ├── LICENSE.md ├── tsconfig.json ├── README.md ├── package.json ├── angular.json └── eslint.config.js /src/assets/demo/demo.scss: -------------------------------------------------------------------------------- 1 | @use './code.scss'; 2 | @use './flags/flags'; 3 | -------------------------------------------------------------------------------- /.postcssrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "@tailwindcss/postcss": {} 4 | } 5 | } -------------------------------------------------------------------------------- /src/assets/demo/flags/flags_responsive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/primefaces/sakai-ng/HEAD/src/assets/demo/flags/flags_responsive.png -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/:path*", 5 | "destination": "/index.html" 6 | } 7 | ], 8 | "trailingSlash": false 9 | } -------------------------------------------------------------------------------- /src/assets/layout/variables/_light.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --surface-ground: var(--p-surface-100); 3 | --code-background: var(--p-surface-900); 4 | --code-color: var(--p-surface-200); 5 | } 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | build 3 | coverage 4 | dist 5 | out 6 | public 7 | styles 8 | node_modules 9 | .vscode 10 | .angular 11 | *.md 12 | *.yml 13 | /tsconfig.json 14 | *.json 15 | -------------------------------------------------------------------------------- /src/assets/layout/variables/_dark.scss: -------------------------------------------------------------------------------- 1 | :root[class*='app-dark'] { 2 | --surface-ground: var(--p-surface-950); 3 | --code-background: var(--p-surface-800); 4 | --code-color: var(--p-surface-100); 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @use './tailwind.css'; 3 | @use './layout/layout.scss'; 4 | @use 'primeicons/primeicons.css'; 5 | @use './demo/demo.scss'; 6 | -------------------------------------------------------------------------------- /src/assets/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | .layout-footer { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | padding: 1rem 0 1rem 0; 6 | gap: 0.5rem; 7 | border-top: 1px solid var(--surface-border); 8 | } 9 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app.config'; 3 | import { AppComponent } from './app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)); 6 | -------------------------------------------------------------------------------- /src/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | standalone: true, 7 | imports: [RouterModule], 8 | template: `` 9 | }) 10 | export class AppComponent {} 11 | -------------------------------------------------------------------------------- /src/assets/tailwind.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'tailwindcss-primeui'; 3 | @custom-variant dark (&:where(.app-dark, .app-dark *)); 4 | 5 | @theme { 6 | --breakpoint-sm: 576px; 7 | --breakpoint-md: 768px; 8 | --breakpoint-lg: 992px; 9 | --breakpoint-xl: 1200px; 10 | --breakpoint-2xl: 1920px; 11 | } -------------------------------------------------------------------------------- /src/assets/layout/layout.scss: -------------------------------------------------------------------------------- 1 | @use './variables/_common'; 2 | @use './variables/_light'; 3 | @use './variables/_dark'; 4 | @use './_mixins'; 5 | @use './_preloading'; 6 | @use './_core'; 7 | @use './_main'; 8 | @use './_topbar'; 9 | @use './_menu'; 10 | @use './_footer'; 11 | @use './_responsive'; 12 | @use './_utils'; 13 | @use './_typography'; 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /src/app/pages/auth/auth.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { Access } from './access'; 3 | import { Login } from './login'; 4 | import { Error } from './error'; 5 | 6 | export default [ 7 | { path: 'access', component: Access }, 8 | { path: 'error', component: Error }, 9 | { path: 'login', component: Login } 10 | ] as Routes; 11 | -------------------------------------------------------------------------------- /src/app/pages/empty/empty.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-empty', 5 | standalone: true, 6 | template: `
7 |
Empty Page
8 |

Use this page to start from scratch and place your custom content.

9 |
` 10 | }) 11 | export class Empty {} 12 | -------------------------------------------------------------------------------- /src/app/layout/component/app.footer.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | standalone: true, 5 | selector: 'app-footer', 6 | template: `` 10 | }) 11 | export class AppFooter {} 12 | -------------------------------------------------------------------------------- /src/app/layout/component/app.sidebar.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef } from '@angular/core'; 2 | import { AppMenu } from './app.menu'; 3 | 4 | @Component({ 5 | selector: 'app-sidebar', 6 | standalone: true, 7 | imports: [AppMenu], 8 | template: `
9 | 10 |
` 11 | }) 12 | export class AppSidebar { 13 | constructor(public el: ElementRef) {} 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/layout/_main.scss: -------------------------------------------------------------------------------- 1 | .layout-main-container { 2 | display: flex; 3 | flex-direction: column; 4 | min-height: 100vh; 5 | justify-content: space-between; 6 | padding: 6rem 2rem 0 2rem; 7 | transition: margin-left var(--layout-section-transition-duration); 8 | } 9 | 10 | .layout-main { 11 | flex: 1 1 auto; 12 | padding-bottom: 2rem; 13 | } 14 | 15 | img { 16 | max-width: none !important; 17 | } 18 | -------------------------------------------------------------------------------- /src/app/pages/pages.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { Documentation } from './documentation/documentation'; 3 | import { Crud } from './crud/crud'; 4 | import { Empty } from './empty/empty'; 5 | 6 | export default [ 7 | { path: 'documentation', component: Documentation }, 8 | { path: 'crud', component: Crud }, 9 | { path: 'empty', component: Empty }, 10 | { path: '**', redirectTo: '/notfound' } 11 | ] as Routes; 12 | -------------------------------------------------------------------------------- /src/assets/demo/code.scss: -------------------------------------------------------------------------------- 1 | pre.app-code { 2 | background-color: var(--code-background); 3 | margin: 0 0 1rem 0; 4 | padding: 0; 5 | border-radius: var(--content-border-radius); 6 | overflow: auto; 7 | 8 | code { 9 | color: var(--code-color); 10 | padding: 1rem; 11 | margin: 0; 12 | line-height: 1.5; 13 | display: block; 14 | font-weight: semibold; 15 | font-family: monaco, Consolas, monospace; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "./out-tsc/app", 7 | "types": [] 8 | }, 9 | "files": [ 10 | "src/main.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/layout/_core.scss: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-size: 14px; 4 | } 5 | 6 | body { 7 | font-family: 'Lato', sans-serif; 8 | color: var(--text-color); 9 | background-color: var(--surface-ground); 10 | margin: 0; 11 | padding: 0; 12 | min-height: 100%; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | line-height: 1.2; 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | } 21 | 22 | .layout-wrapper { 23 | min-height: 100vh; 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/layout/_utils.scss: -------------------------------------------------------------------------------- 1 | /* Utils */ 2 | .clearfix:after { 3 | content: ' '; 4 | display: block; 5 | clear: both; 6 | } 7 | 8 | .card { 9 | background: var(--surface-card); 10 | padding: 2rem; 11 | margin-bottom: 2rem; 12 | border-radius: var(--content-border-radius); 13 | 14 | &:last-child { 15 | margin-bottom: 0; 16 | } 17 | } 18 | 19 | .p-toast { 20 | &.p-toast-top-right, 21 | &.p-toast-top-left, 22 | &.p-toast-top-center { 23 | top: 100px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/layout/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin focused() { 2 | outline-width: var(--focus-ring-width); 3 | outline-style: var(--focus-ring-style); 4 | outline-color: var(--focus-ring-color); 5 | outline-offset: var(--focus-ring-offset); 6 | box-shadow: var(--focus-ring-shadow); 7 | transition: 8 | box-shadow var(--transition-duration), 9 | outline-color var(--transition-duration); 10 | } 11 | 12 | @mixin focused-inset() { 13 | outline-offset: -1px; 14 | box-shadow: inset var(--focus-ring-shadow); 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "./out-tsc/spec", 7 | "types": [ 8 | "jasmine" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sakai - PrimeNG 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/app/pages/service/icon.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | @Injectable() 6 | export class IconService { 7 | constructor(private http: HttpClient) {} 8 | 9 | icons!: any[]; 10 | 11 | selectedIcon: any; 12 | 13 | apiUrl = 'assets/demo/data/icons.json'; 14 | 15 | getIcons() { 16 | return this.http.get(this.apiUrl).pipe( 17 | map((response: any) => { 18 | this.icons = response.icons; 19 | return this.icons; 20 | }) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "tabWidth": 4, 4 | "trailingComma": "none", 5 | "semi": true, 6 | "singleQuote": true, 7 | "printWidth": 250, 8 | "bracketSameLine": false, 9 | "overrides": [ 10 | { 11 | "files": ["*.ts", "*.mts", "*.d.ts"], 12 | "options": { 13 | "parser": "typescript" 14 | } 15 | }, 16 | { 17 | "files": ["*.html"], 18 | "options": { 19 | "parser": "html" 20 | } 21 | }, 22 | { 23 | "files": ["*.component.html"], 24 | "options": { 25 | "parser": "angular" 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | .vscode 40 | 41 | # System files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /src/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient, withFetch } from '@angular/common/http'; 2 | import { ApplicationConfig } from '@angular/core'; 3 | import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; 4 | import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router'; 5 | import Aura from '@primeuix/themes/aura'; 6 | import { providePrimeNG } from 'primeng/config'; 7 | import { appRoutes } from './app.routes'; 8 | 9 | export const appConfig: ApplicationConfig = { 10 | providers: [ 11 | provideRouter(appRoutes, withInMemoryScrolling({ anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled' }), withEnabledBlockingInitialNavigation()), 12 | provideHttpClient(withFetch()), 13 | provideAnimationsAsync(), 14 | providePrimeNG({ theme: { preset: Aura, options: { darkModeSelector: '.app-dark' } } }) 15 | ] 16 | }; 17 | -------------------------------------------------------------------------------- /src/assets/layout/variables/_common.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: var(--p-primary-color); 3 | --primary-contrast-color: var(--p-primary-contrast-color); 4 | --text-color: var(--p-text-color); 5 | --text-color-secondary: var(--p-text-muted-color); 6 | --surface-border: var(--p-content-border-color); 7 | --surface-card: var(--p-content-background); 8 | --surface-hover: var(--p-content-hover-background); 9 | --surface-overlay: var(--p-overlay-popover-background); 10 | --transition-duration: var(--p-transition-duration); 11 | --maskbg: var(--p-mask-background); 12 | --content-border-radius: var(--p-content-border-radius); 13 | --layout-section-transition-duration: 0.2s; 14 | --element-transition-duration: var(--p-transition-duration); 15 | --focus-ring-width: var(--p-focus-ring-width); 16 | --focus-ring-style: var(--p-focus-ring-style); 17 | --focus-ring-color: var(--p-focus-ring-color); 18 | --focus-ring-offset: var(--p-focus-ring-offset); 19 | --focus-ring-shadow: var(--p-focus-ring-shadow); 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/layout/_preloading.scss: -------------------------------------------------------------------------------- 1 | .preloader { 2 | position: fixed; 3 | z-index: 999999; 4 | background: #edf1f5; 5 | width: 100%; 6 | height: 100%; 7 | } 8 | .preloader-content { 9 | border: 0 solid transparent; 10 | border-radius: 50%; 11 | width: 150px; 12 | height: 150px; 13 | position: absolute; 14 | top: calc(50vh - 75px); 15 | left: calc(50vw - 75px); 16 | } 17 | 18 | .preloader-content:before, .preloader-content:after{ 19 | content: ''; 20 | border: 1em solid var(--primary-color); 21 | border-radius: 50%; 22 | width: inherit; 23 | height: inherit; 24 | position: absolute; 25 | top: 0; 26 | left: 0; 27 | animation: loader 2s linear infinite; 28 | opacity: 0; 29 | } 30 | 31 | .preloader-content:before{ 32 | animation-delay: 0.5s; 33 | } 34 | 35 | @keyframes loader{ 36 | 0%{ 37 | transform: scale(0); 38 | opacity: 0; 39 | } 40 | 50%{ 41 | opacity: 1; 42 | } 43 | 100%{ 44 | transform: scale(1); 45 | opacity: 0; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { AppLayout } from './app/layout/component/app.layout'; 3 | import { Dashboard } from './app/pages/dashboard/dashboard'; 4 | import { Documentation } from './app/pages/documentation/documentation'; 5 | import { Landing } from './app/pages/landing/landing'; 6 | import { Notfound } from './app/pages/notfound/notfound'; 7 | 8 | export const appRoutes: Routes = [ 9 | { 10 | path: '', 11 | component: AppLayout, 12 | children: [ 13 | { path: '', component: Dashboard }, 14 | { path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') }, 15 | { path: 'documentation', component: Documentation }, 16 | { path: 'pages', loadChildren: () => import('./app/pages/pages.routes') } 17 | ] 18 | }, 19 | { path: 'landing', component: Landing }, 20 | { path: 'notfound', component: Notfound }, 21 | { path: 'auth', loadChildren: () => import('./app/pages/auth/auth.routes') }, 22 | { path: '**', redirectTo: '/notfound' } 23 | ]; 24 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/dashboard.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NotificationsWidget } from './components/notificationswidget'; 3 | import { StatsWidget } from './components/statswidget'; 4 | import { RecentSalesWidget } from './components/recentsaleswidget'; 5 | import { BestSellingWidget } from './components/bestsellingwidget'; 6 | import { RevenueStreamWidget } from './components/revenuestreamwidget'; 7 | 8 | @Component({ 9 | selector: 'app-dashboard', 10 | imports: [StatsWidget, RecentSalesWidget, BestSellingWidget, RevenueStreamWidget, NotificationsWidget], 11 | template: ` 12 |
13 | 14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | ` 24 | }) 25 | export class Dashboard {} 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2022 PrimeTek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/layout/_typography.scss: -------------------------------------------------------------------------------- 1 | h1, 2 | h2, 3 | h3, 4 | h4, 5 | h5, 6 | h6 { 7 | margin: 1.5rem 0 1rem 0; 8 | font-family: inherit; 9 | font-weight: 700; 10 | line-height: 1.5; 11 | color: var(--text-color); 12 | 13 | &:first-child { 14 | margin-top: 0; 15 | } 16 | } 17 | 18 | h1 { 19 | font-size: 2.5rem; 20 | } 21 | 22 | h2 { 23 | font-size: 2rem; 24 | } 25 | 26 | h3 { 27 | font-size: 1.75rem; 28 | } 29 | 30 | h4 { 31 | font-size: 1.5rem; 32 | } 33 | 34 | h5 { 35 | font-size: 1.25rem; 36 | } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | } 41 | 42 | mark { 43 | background: #fff8e1; 44 | padding: 0.25rem 0.4rem; 45 | border-radius: var(--content-border-radius); 46 | font-family: monospace; 47 | } 48 | 49 | blockquote { 50 | margin: 1rem 0; 51 | padding: 0 2rem; 52 | border-left: 4px solid #90a4ae; 53 | } 54 | 55 | hr { 56 | border-top: solid var(--surface-border); 57 | border-width: 1px 0 0 0; 58 | margin: 1rem 0; 59 | } 60 | 61 | p { 62 | margin: 0 0 1rem 0; 63 | line-height: 1.5; 64 | 65 | &:last-child { 66 | margin-bottom: 0; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "compileOnSave": false, 5 | "compilerOptions": { 6 | "outDir": "./dist/out-tsc", 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "isolatedModules": true, 14 | "esModuleInterop": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "bundler", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "baseUrl": "./", 21 | "paths": { 22 | "@/*": [ 23 | "src/app/*" 24 | ] 25 | } 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/app/pages/landing/landing.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { RippleModule } from 'primeng/ripple'; 4 | import { StyleClassModule } from 'primeng/styleclass'; 5 | import { ButtonModule } from 'primeng/button'; 6 | import { DividerModule } from 'primeng/divider'; 7 | import { TopbarWidget } from './components/topbarwidget.component'; 8 | import { HeroWidget } from './components/herowidget'; 9 | import { FeaturesWidget } from './components/featureswidget'; 10 | import { HighlightsWidget } from './components/highlightswidget'; 11 | import { PricingWidget } from './components/pricingwidget'; 12 | import { FooterWidget } from './components/footerwidget'; 13 | 14 | @Component({ 15 | selector: 'app-landing', 16 | standalone: true, 17 | imports: [RouterModule, TopbarWidget, HeroWidget, FeaturesWidget, HighlightsWidget, PricingWidget, FooterWidget, RippleModule, StyleClassModule, ButtonModule, DividerModule], 18 | template: ` 19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | ` 30 | }) 31 | export class Landing {} 32 | -------------------------------------------------------------------------------- /src/app/layout/component/app.floatingconfigurator.ts: -------------------------------------------------------------------------------- 1 | import {Component, computed, inject, input} from '@angular/core'; 2 | import { ButtonModule } from 'primeng/button'; 3 | import { StyleClassModule } from 'primeng/styleclass'; 4 | import { AppConfigurator } from './app.configurator'; 5 | import { LayoutService } from '../service/layout.service'; 6 | import {CommonModule} from "@angular/common"; 7 | 8 | @Component({ 9 | selector: 'app-floating-configurator', 10 | imports: [CommonModule, ButtonModule, StyleClassModule, AppConfigurator], 11 | template: ` 12 |
13 | 14 |
15 | 16 | 17 |
18 |
19 | ` 20 | }) 21 | export class AppFloatingConfigurator { 22 | LayoutService = inject(LayoutService); 23 | 24 | float = input(true); 25 | 26 | isDarkTheme = computed(() => this.LayoutService.layoutConfig().darkTheme); 27 | 28 | toggleDarkMode() { 29 | this.LayoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/herowidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ButtonModule } from 'primeng/button'; 3 | import { RippleModule } from 'primeng/ripple'; 4 | 5 | @Component({ 6 | selector: 'hero-widget', 7 | imports: [ButtonModule, RippleModule], 8 | template: ` 9 |
14 |
15 |

Eu sem integereget magna fermentum

16 |

Sed blandit libero volutpat sed cras. Fames ac turpis egestas integer. Placerat in egestas erat...

17 | 18 |
19 |
20 | Hero Image 21 |
22 |
23 | ` 24 | }) 25 | export class HeroWidget {} 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sakai19 2 | 3 | This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.5. 4 | 5 | ## Development server 6 | 7 | To start a local development server, run: 8 | 9 | ```bash 10 | ng serve 11 | ``` 12 | 13 | Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. 14 | 15 | ## Code scaffolding 16 | 17 | Angular CLI includes powerful code scaffolding tools. To generate a new component, run: 18 | 19 | ```bash 20 | ng generate component component-name 21 | ``` 22 | 23 | For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: 24 | 25 | ```bash 26 | ng generate --help 27 | ``` 28 | 29 | ## Building 30 | 31 | To build the project run: 32 | 33 | ```bash 34 | ng build 35 | ``` 36 | 37 | This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. 38 | 39 | ## Running unit tests 40 | 41 | To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: 42 | 43 | ```bash 44 | ng test 45 | ``` 46 | 47 | ## Running end-to-end tests 48 | 49 | For end-to-end (e2e) testing, run: 50 | 51 | ```bash 52 | ng e2e 53 | ``` 54 | 55 | Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. 56 | 57 | ## Additional Resources 58 | 59 | For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sakai-ng", 3 | "version": "20.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "format": "prettier --write \"**/*.{js,mjs,ts,mts,d.ts,html}\" --cache", 10 | "test": "ng test" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^20", 15 | "@angular/common": "^20", 16 | "@angular/compiler": "^20", 17 | "@angular/core": "^20", 18 | "@angular/forms": "^20", 19 | "@angular/platform-browser": "^20", 20 | "@angular/platform-browser-dynamic": "^20", 21 | "@angular/router": "^20", 22 | "@primeuix/themes": "^1.2.1", 23 | "@tailwindcss/postcss": "^4.1.11", 24 | "chart.js": "4.4.2", 25 | "primeclt": "^0.1.5", 26 | "primeicons": "^7.0.0", 27 | "primeng": "^20", 28 | "rxjs": "~7.8.2", 29 | "tailwindcss-primeui": "^0.6.1", 30 | "tslib": "^2.8.1", 31 | "zone.js": "~0.15.1" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "^20", 35 | "@angular/cli": "^20", 36 | "@angular/compiler-cli": "^20", 37 | "@types/jasmine": "~5.1.0", 38 | "autoprefixer": "^10.4.20", 39 | "eslint": "^9.30.1", 40 | "eslint-config-prettier": "^10.1.5", 41 | "eslint-plugin-import": "^2.32.0", 42 | "eslint-plugin-prefer-arrow": "^1.2.3", 43 | "eslint-plugin-prettier": "^5.5.1", 44 | "jasmine-core": "~5.8.0", 45 | "karma": "~6.4.4", 46 | "karma-chrome-launcher": "~3.2.0", 47 | "karma-coverage": "~2.2.1", 48 | "karma-jasmine": "~5.1.0", 49 | "karma-jasmine-html-reporter": "~2.1.0", 50 | "postcss": "^8.5.6", 51 | "prettier": "^3.6.2", 52 | "tailwindcss": "^4.1.11", 53 | "typescript": "~5.8.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/pages/uikit/uikit.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { ButtonDemo } from './buttondemo'; 3 | import { ChartDemo } from './chartdemo'; 4 | import { FileDemo } from './filedemo'; 5 | import { FormLayoutDemo } from './formlayoutdemo'; 6 | import { InputDemo } from './inputdemo'; 7 | import { ListDemo } from './listdemo'; 8 | import { MediaDemo } from './mediademo'; 9 | import { MessagesDemo } from './messagesdemo'; 10 | import { MiscDemo } from './miscdemo'; 11 | import { PanelsDemo } from './panelsdemo'; 12 | import { TimelineDemo } from './timelinedemo'; 13 | import { TableDemo } from './tabledemo'; 14 | import { OverlayDemo } from './overlaydemo'; 15 | import { TreeDemo } from './treedemo'; 16 | import { MenuDemo } from './menudemo'; 17 | 18 | export default [ 19 | { path: 'button', data: { breadcrumb: 'Button' }, component: ButtonDemo }, 20 | { path: 'charts', data: { breadcrumb: 'Charts' }, component: ChartDemo }, 21 | { path: 'file', data: { breadcrumb: 'File' }, component: FileDemo }, 22 | { path: 'formlayout', data: { breadcrumb: 'Form Layout' }, component: FormLayoutDemo }, 23 | { path: 'input', data: { breadcrumb: 'Input' }, component: InputDemo }, 24 | { path: 'list', data: { breadcrumb: 'List' }, component: ListDemo }, 25 | { path: 'media', data: { breadcrumb: 'Media' }, component: MediaDemo }, 26 | { path: 'message', data: { breadcrumb: 'Message' }, component: MessagesDemo }, 27 | { path: 'misc', data: { breadcrumb: 'Misc' }, component: MiscDemo }, 28 | { path: 'panel', data: { breadcrumb: 'Panel' }, component: PanelsDemo }, 29 | { path: 'timeline', data: { breadcrumb: 'Timeline' }, component: TimelineDemo }, 30 | { path: 'table', data: { breadcrumb: 'Table' }, component: TableDemo }, 31 | { path: 'overlay', data: { breadcrumb: 'Overlay' }, component: OverlayDemo }, 32 | { path: 'tree', data: { breadcrumb: 'Tree' }, component: TreeDemo }, 33 | { path: 'menu', data: { breadcrumb: 'Menu' }, component: MenuDemo }, 34 | { path: '**', redirectTo: '/notfound' } 35 | ] as Routes; 36 | -------------------------------------------------------------------------------- /src/app/pages/auth/error.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { ButtonModule } from 'primeng/button'; 4 | import { RippleModule } from 'primeng/ripple'; 5 | import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; 6 | 7 | @Component({ 8 | selector: 'app-error', 9 | imports: [ButtonModule, RippleModule, RouterModule, AppFloatingConfigurator, ButtonModule], 10 | standalone: true, 11 | template: ` 12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |

Error Occured

21 | Requested resource is not available. 22 | Error 23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 |
` 31 | }) 32 | export class Error {} 33 | -------------------------------------------------------------------------------- /src/app/pages/auth/access.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { ButtonModule } from 'primeng/button'; 4 | import { RippleModule } from 'primeng/ripple'; 5 | import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; 6 | 7 | @Component({ 8 | selector: 'app-access', 9 | standalone: true, 10 | imports: [ButtonModule, RouterModule, RippleModule, AppFloatingConfigurator, ButtonModule], 11 | template: ` 12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |

Access Denied

21 | You do not have the necessary permisions. Please contact admins. 22 | Access denied 23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 |
` 31 | }) 32 | export class Access {} 33 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/components/recentsaleswidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RippleModule } from 'primeng/ripple'; 3 | import { TableModule } from 'primeng/table'; 4 | import { ButtonModule } from 'primeng/button'; 5 | import { CommonModule } from '@angular/common'; 6 | import { Product, ProductService } from '../../service/product.service'; 7 | 8 | @Component({ 9 | standalone: true, 10 | selector: 'app-recent-sales-widget', 11 | imports: [CommonModule, TableModule, ButtonModule, RippleModule], 12 | template: `
13 |
Recent Sales
14 | 15 | 16 | 17 | Image 18 | Name 19 | Price 20 | View 21 | 22 | 23 | 24 | 25 | 26 | {{ product.name }} 27 | 28 | {{ product.name }} 29 | {{ product.price | currency: 'USD' }} 30 | 31 | 32 | 33 | 34 | 35 | 36 |
`, 37 | providers: [ProductService] 38 | }) 39 | export class RecentSalesWidget { 40 | products!: Product[]; 41 | 42 | constructor(private productService: ProductService) {} 43 | 44 | ngOnInit() { 45 | this.productService.getProductsSmall().then((data) => (this.products = data)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/pages/uikit/filedemo.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component } from '@angular/core'; 3 | import { MessageService } from 'primeng/api'; 4 | import { ButtonModule } from 'primeng/button'; 5 | import { FileUploadModule } from 'primeng/fileupload'; 6 | import { ToastModule } from 'primeng/toast'; 7 | 8 | @Component({ 9 | selector: 'app-file-demo', 10 | standalone: true, 11 | imports: [CommonModule, FileUploadModule, ToastModule, ButtonModule], 12 | template: ` 13 |
14 |
15 |
16 |
Advanced
17 | 18 | 19 |
Drag and drop files to here to upload.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Basic
27 |
28 | 29 | 30 |
31 |
32 |
33 |
`, 34 | providers: [MessageService] 35 | }) 36 | export class FileDemo { 37 | uploadedFiles: any[] = []; 38 | 39 | constructor(private messageService: MessageService) {} 40 | 41 | onUpload(event: any) { 42 | for (const file of event.files) { 43 | this.uploadedFiles.push(file); 44 | } 45 | 46 | this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded' }); 47 | } 48 | 49 | onBasicUpload() { 50 | this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded with Basic Mode' }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/pages/uikit/treedemo.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject, OnInit } from '@angular/core'; 2 | import { TreeNode } from 'primeng/api'; 3 | import { TreeModule } from 'primeng/tree'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { TreeTableModule } from 'primeng/treetable'; 6 | import { CommonModule } from '@angular/common'; 7 | import { NodeService } from '../service/node.service'; 8 | 9 | @Component({ 10 | selector: 'app-tree-demo', 11 | standalone: true, 12 | imports: [CommonModule, FormsModule, TreeModule, TreeTableModule], 13 | template: ` 14 |
15 |
Tree
16 | 17 |
18 | 19 |
20 |
TreeTable
21 | 22 | 23 | 24 | 25 | {{ col.header }} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {{ rowData[col.field] }} 36 | 37 | 38 | 39 | 40 | 41 |
42 | `, 43 | providers: [NodeService] 44 | }) 45 | export class TreeDemo implements OnInit { 46 | treeValue: TreeNode[] = []; 47 | 48 | treeTableValue: TreeNode[] = []; 49 | 50 | selectedTreeValue: TreeNode[] = []; 51 | 52 | selectedTreeTableValue = {}; 53 | 54 | cols: any[] = []; 55 | 56 | nodeService = inject(NodeService); 57 | 58 | ngOnInit() { 59 | this.nodeService.getFiles().then((files) => (this.treeValue = files)); 60 | this.nodeService.getTreeTableNodes().then((files: any) => (this.treeTableValue = files)); 61 | 62 | this.cols = [ 63 | { field: 'name', header: 'Name' }, 64 | { field: 'size', header: 'Size' }, 65 | { field: 'type', header: 'Type' } 66 | ]; 67 | 68 | this.selectedTreeTableValue = { 69 | '0-0': { 70 | partialChecked: false, 71 | checked: true 72 | } 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/highlightswidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'highlights-widget', 5 | template: ` 6 |
7 |
8 |
Powerful Everywhere
9 | Amet consectetur adipiscing elit... 10 |
11 | 12 |
13 |
14 | mockup mobile 15 |
16 | 17 |
18 |
19 | 20 |
21 |
Congue Quisque Egestas
22 | Lectus arcu bibendum at varius vel pharetra vel turpis nunc. Eget aliquet nibh praesent tristique magna sit amet purus gravida. Sit amet mattis vulputate enim nulla aliquet. 25 |
26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 |
Celerisque Eu Ultrices
34 | Adipiscing commodo elit at imperdiet dui. Viverra nibh cras pulvinar mattis nunc sed blandit libero. Suspendisse in est ante in. Mauris pharetra et ultrices neque ornare aenean euismod elementum nisi. 37 |
38 | 39 |
40 | mockup 41 |
42 |
43 |
44 | ` 45 | }) 46 | export class HighlightsWidget {} 47 | -------------------------------------------------------------------------------- /src/assets/layout/_responsive.scss: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 1960px) { 2 | .layout-main, 3 | .landing-wrapper { 4 | width: 1504px; 5 | margin-left: auto !important; 6 | margin-right: auto !important; 7 | } 8 | } 9 | 10 | @media (min-width: 992px) { 11 | .layout-wrapper { 12 | &.layout-overlay { 13 | .layout-main-container { 14 | margin-left: 0; 15 | padding-left: 2rem; 16 | } 17 | 18 | .layout-sidebar { 19 | transform: translateX(-100%); 20 | left: 0; 21 | top: 0; 22 | height: 100vh; 23 | border-top-left-radius: 0; 24 | border-bottom-left-radius: 0; 25 | border-right: 1px solid var(--surface-border); 26 | transition: 27 | transform 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99), 28 | left 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99); 29 | box-shadow: 30 | 0px 3px 5px rgba(0, 0, 0, 0.02), 31 | 0px 0px 2px rgba(0, 0, 0, 0.05), 32 | 0px 1px 4px rgba(0, 0, 0, 0.08); 33 | } 34 | 35 | &.layout-overlay-active { 36 | .layout-sidebar { 37 | transform: translateX(0); 38 | } 39 | } 40 | } 41 | 42 | &.layout-static { 43 | .layout-main-container { 44 | margin-left: 22rem; 45 | } 46 | 47 | &.layout-static-inactive { 48 | .layout-sidebar { 49 | transform: translateX(-100%); 50 | left: 0; 51 | } 52 | 53 | .layout-main-container { 54 | margin-left: 0; 55 | padding-left: 2rem; 56 | } 57 | } 58 | } 59 | 60 | .layout-mask { 61 | display: none; 62 | } 63 | } 64 | } 65 | 66 | @media (max-width: 991px) { 67 | .blocked-scroll { 68 | overflow: hidden; 69 | } 70 | 71 | .layout-wrapper { 72 | .layout-main-container { 73 | margin-left: 0; 74 | padding-left: 2rem; 75 | } 76 | 77 | .layout-sidebar { 78 | transform: translateX(-100%); 79 | left: 0; 80 | top: 0; 81 | height: 100vh; 82 | border-top-left-radius: 0; 83 | border-bottom-left-radius: 0; 84 | transition: 85 | transform 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99), 86 | left 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99); 87 | } 88 | 89 | .layout-mask { 90 | display: none; 91 | position: fixed; 92 | top: 0; 93 | left: 0; 94 | z-index: 998; 95 | width: 100%; 96 | height: 100%; 97 | background-color: var(--maskbg); 98 | } 99 | 100 | &.layout-mobile-active { 101 | .layout-sidebar { 102 | transform: translateX(0); 103 | } 104 | 105 | .layout-mask { 106 | display: block; 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "sakai-ng": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:application", 19 | "options": { 20 | "outputPath": "dist/sakai-ng", 21 | "index": "src/index.html", 22 | "browser": "src/main.ts", 23 | "polyfills": [ 24 | "zone.js" 25 | ], 26 | "tsConfig": "tsconfig.app.json", 27 | "inlineStyleLanguage": "scss", 28 | "assets": [ 29 | { 30 | "glob": "**/*", 31 | "input": "public" 32 | } 33 | ], 34 | "styles": [ 35 | "src/assets/styles.scss" 36 | ], 37 | "scripts": [] 38 | }, 39 | "configurations": { 40 | "production": { 41 | "outputHashing": "all" 42 | }, 43 | "development": { 44 | "optimization": false, 45 | "extractLicenses": false, 46 | "sourceMap": true 47 | } 48 | }, 49 | "defaultConfiguration": "production" 50 | }, 51 | "serve": { 52 | "builder": "@angular-devkit/build-angular:dev-server", 53 | "configurations": { 54 | "production": { 55 | "buildTarget": "sakai-ng:build:production" 56 | }, 57 | "development": { 58 | "buildTarget": "sakai-ng:build:development" 59 | } 60 | }, 61 | "defaultConfiguration": "development" 62 | }, 63 | "extract-i18n": { 64 | "builder": "@angular-devkit/build-angular:extract-i18n" 65 | }, 66 | "test": { 67 | "builder": "@angular-devkit/build-angular:karma", 68 | "options": { 69 | "polyfills": [ 70 | "zone.js", 71 | "zone.js/testing" 72 | ], 73 | "tsConfig": "tsconfig.spec.json", 74 | "inlineStyleLanguage": "scss", 75 | "assets": [ 76 | { 77 | "glob": "**/*", 78 | "input": "public" 79 | } 80 | ], 81 | "styles": [ 82 | "src/assets/styles.scss" 83 | ], 84 | "scripts": [] 85 | } 86 | } 87 | } 88 | } 89 | }, 90 | "cli": { 91 | "analytics": false 92 | } 93 | } -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | root: true, 3 | ignorePatterns: ['**/dist/**'], 4 | plugins: ['prettier'], 5 | extends: ['prettier'], 6 | rules: { 7 | 'padding-line-between-statements': [ 8 | 'error', 9 | { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, 10 | { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] }, 11 | { blankLine: 'any', prev: ['case', 'default'], next: 'break' }, 12 | { blankLine: 'any', prev: 'case', next: 'case' }, 13 | { blankLine: 'always', prev: '*', next: 'return' }, 14 | { blankLine: 'always', prev: 'block', next: '*' }, 15 | { blankLine: 'always', prev: '*', next: 'block' }, 16 | { blankLine: 'always', prev: 'block-like', next: '*' }, 17 | { blankLine: 'always', prev: '*', next: 'block-like' }, 18 | { blankLine: 'always', prev: ['import'], next: ['const', 'let', 'var'] } 19 | ] 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.ts'], 24 | parserOptions: { 25 | project: ['tsconfig.json', 'e2e/tsconfig.json'], 26 | createDefaultProgram: true 27 | }, 28 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@angular-eslint/recommended', 'plugin:@angular-eslint/template/process-inline-templates', 'prettier'], 29 | rules: { 30 | '@angular-eslint/component-selector': [ 31 | 'error', 32 | { 33 | type: 'element', 34 | prefix: 'p', 35 | style: 'kebab-case' 36 | } 37 | ], 38 | '@angular-eslint/directive-selector': [ 39 | 'error', 40 | { 41 | type: 'attribute', 42 | prefix: 'p', 43 | style: 'camelCase' 44 | } 45 | ], 46 | '@angular-eslint/component-class-suffix': [ 47 | 'error', 48 | { 49 | suffixes: [''] 50 | } 51 | ], 52 | '@angular-eslint/template/eqeqeq': [ 53 | 'error', 54 | { 55 | allowNullOrUndefined: true 56 | } 57 | ], 58 | '@angular-eslint/no-host-metadata-property': 'off', 59 | '@angular-eslint/no-output-on-prefix': 'off', 60 | '@typescript-eslint/ban-types': 'off', 61 | '@typescript-eslint/no-explicit-any': 'off', 62 | '@typescript-eslint/no-inferrable-types': 'off', 63 | 'arrow-body-style': ['error', 'as-needed'], 64 | curly: 0, 65 | '@typescript-eslint/member-ordering': [ 66 | 'error', 67 | { 68 | default: ['public-static-field', 'static-field', 'instance-field', 'public-instance-method', 'public-static-field'] 69 | } 70 | ], 71 | 'no-console': 0, 72 | 'prefer-const': 0 73 | } 74 | }, 75 | { 76 | files: ['*.html'], 77 | extends: ['plugin:@angular-eslint/template/recommended', 'prettier'], 78 | rules: {} 79 | }, 80 | { 81 | files: ['*.js'], 82 | rules: { 83 | parserOptions: { 84 | allowImportExportEverywhere: true 85 | } 86 | } 87 | } 88 | ] 89 | }; 90 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/components/statswidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | @Component({ 5 | standalone: true, 6 | selector: 'app-stats-widget', 7 | imports: [CommonModule], 8 | template: `
9 |
10 |
11 |
12 | Orders 13 |
152
14 |
15 |
16 | 17 |
18 |
19 | 24 new 20 | since last visit 21 |
22 |
23 |
24 |
25 |
26 |
27 | Revenue 28 |
$2.100
29 |
30 |
31 | 32 |
33 |
34 | %52+ 35 | since last week 36 |
37 |
38 |
39 |
40 |
41 |
42 | Customers 43 |
28441
44 |
45 |
46 | 47 |
48 |
49 | 520 50 | newly registered 51 |
52 |
53 |
54 |
55 |
56 |
57 | Comments 58 |
152 Unread
59 |
60 |
61 | 62 |
63 |
64 | 85 65 | responded 66 |
67 |
` 68 | }) 69 | export class StatsWidget {} 70 | -------------------------------------------------------------------------------- /src/app/pages/uikit/messagesdemo.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { MessageService, ToastMessageOptions } from 'primeng/api'; 5 | import { ButtonModule } from 'primeng/button'; 6 | import { InputTextModule } from 'primeng/inputtext'; 7 | import { MessageModule } from 'primeng/message'; 8 | import { ToastModule } from 'primeng/toast'; 9 | 10 | @Component({ 11 | selector: 'app-messages-demo', 12 | standalone: true, 13 | imports: [CommonModule, ToastModule, ButtonModule, InputTextModule, MessageModule, FormsModule], 14 | template: ` 15 |
16 |
17 |
18 |
Toast
19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
Inline
28 |
29 | 30 | Username is required 31 |
32 |
33 | 34 | Email is required 35 |
36 |
37 |
38 |
39 |
40 |
Message
41 |
42 | Success Message 43 | Info Message 44 | Warn Message 45 | Error Message 46 | Secondary Message 47 | Contrast Message 48 |
49 |
50 |
51 |
52 | `, 53 | providers: [MessageService] 54 | }) 55 | export class MessagesDemo { 56 | msgs: ToastMessageOptions[] | null = []; 57 | 58 | username: string | undefined; 59 | 60 | email: string | undefined; 61 | 62 | constructor(private service: MessageService) {} 63 | 64 | showInfoViaToast() { 65 | this.service.add({ severity: 'info', summary: 'Info Message', detail: 'PrimeNG rocks' }); 66 | } 67 | 68 | showWarnViaToast() { 69 | this.service.add({ severity: 'warn', summary: 'Warn Message', detail: 'There are unsaved changes' }); 70 | } 71 | 72 | showErrorViaToast() { 73 | this.service.add({ severity: 'error', summary: 'Error Message', detail: 'Validation failed' }); 74 | } 75 | 76 | showSuccessViaToast() { 77 | this.service.add({ severity: 'success', summary: 'Success Message', detail: 'Message sent' }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/components/revenuestreamwidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ChartModule } from 'primeng/chart'; 3 | import { debounceTime, Subscription } from 'rxjs'; 4 | import { LayoutService } from '../../../layout/service/layout.service'; 5 | 6 | @Component({ 7 | standalone: true, 8 | selector: 'app-revenue-stream-widget', 9 | imports: [ChartModule], 10 | template: `
11 |
Revenue Stream
12 | 13 |
` 14 | }) 15 | export class RevenueStreamWidget { 16 | chartData: any; 17 | 18 | chartOptions: any; 19 | 20 | subscription!: Subscription; 21 | 22 | constructor(public layoutService: LayoutService) { 23 | this.subscription = this.layoutService.configUpdate$.pipe(debounceTime(25)).subscribe(() => { 24 | this.initChart(); 25 | }); 26 | } 27 | 28 | ngOnInit() { 29 | this.initChart(); 30 | } 31 | 32 | initChart() { 33 | const documentStyle = getComputedStyle(document.documentElement); 34 | const textColor = documentStyle.getPropertyValue('--text-color'); 35 | const borderColor = documentStyle.getPropertyValue('--surface-border'); 36 | const textMutedColor = documentStyle.getPropertyValue('--text-color-secondary'); 37 | 38 | this.chartData = { 39 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 40 | datasets: [ 41 | { 42 | type: 'bar', 43 | label: 'Subscriptions', 44 | backgroundColor: documentStyle.getPropertyValue('--p-primary-400'), 45 | data: [4000, 10000, 15000, 4000], 46 | barThickness: 32 47 | }, 48 | { 49 | type: 'bar', 50 | label: 'Advertising', 51 | backgroundColor: documentStyle.getPropertyValue('--p-primary-300'), 52 | data: [2100, 8400, 2400, 7500], 53 | barThickness: 32 54 | }, 55 | { 56 | type: 'bar', 57 | label: 'Affiliate', 58 | backgroundColor: documentStyle.getPropertyValue('--p-primary-200'), 59 | data: [4100, 5200, 3400, 7400], 60 | borderRadius: { 61 | topLeft: 8, 62 | topRight: 8, 63 | bottomLeft: 0, 64 | bottomRight: 0 65 | }, 66 | borderSkipped: false, 67 | barThickness: 32 68 | } 69 | ] 70 | }; 71 | 72 | this.chartOptions = { 73 | maintainAspectRatio: false, 74 | aspectRatio: 0.8, 75 | plugins: { 76 | legend: { 77 | labels: { 78 | color: textColor 79 | } 80 | } 81 | }, 82 | scales: { 83 | x: { 84 | stacked: true, 85 | ticks: { 86 | color: textMutedColor 87 | }, 88 | grid: { 89 | color: 'transparent', 90 | borderColor: 'transparent' 91 | } 92 | }, 93 | y: { 94 | stacked: true, 95 | ticks: { 96 | color: textMutedColor 97 | }, 98 | grid: { 99 | color: borderColor, 100 | borderColor: 'transparent', 101 | drawTicks: false 102 | } 103 | } 104 | } 105 | }; 106 | } 107 | 108 | ngOnDestroy() { 109 | if (this.subscription) { 110 | this.subscription.unsubscribe(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/app/pages/documentation/documentation.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'app-documentation', 6 | standalone: true, 7 | imports: [CommonModule], 8 | template: ` 9 |
10 |
Documentation
11 |
Get Started
12 |

Sakai is an application template for Angular and is distributed as a CLI project. Current versions is Angular v20 with PrimeNG v20. In case CLI is not installed already, use the command below to set it up.

13 |
14 | npm install -g @angular/cli
15 |

16 | Once CLI is ready in your system, extract the contents of the zip file distribution, cd to the directory, install the libraries from npm and then execute "ng serve" to run the application in your local environment. 17 |

18 |
19 | git clone https://github.com/primefaces/sakai-ng
20 | npm install
21 | ng serve
22 | 23 |

The application should run at http://localhost:4200/ to view the application in your local environment.

24 | 25 |
Structure
26 |

Templates consists of a couple folders, demos and layout have been separated so that you can easily identify what is necessary for your application.

27 |
    28 |
  • src/app/layout: Main layout files, needs to be present.
  • 29 |
  • src/app/pages: Demo content like Dashboard.
  • 30 |
  • src/assets/demo: Assets used in demos
  • 31 |
  • src/assets/layout: SCSS files of the main layout
  • 32 |
33 | 34 |
Menu
35 |

36 | Main menu is defined at src/app/layout/component/app.menu.ts file. Update the 37 | model property to define your own menu items. 38 |

39 | 40 |
Layout Service
41 |

42 | src/app/layout/service/layout.service.ts is a service that manages layout state changes, including dark mode, PrimeNG theme, menu modes, and states. 43 |

44 | 45 |
Tailwind CSS
46 |

The demo pages are developed with Tailwind CSS however the core application shell uses custom CSS.

47 | 48 |
Variables
49 |

50 | CSS variables used in the template are derived from the applied PrimeNG theme. Customize them through the CSS variables in src/assets/layout/variables. 51 |

52 |
53 | `, 54 | styles: ` 55 | @media screen and (max-width: 991px) { 56 | .video-container { 57 | position: relative; 58 | width: 100%; 59 | height: 0; 60 | padding-bottom: 56.25%; 61 | 62 | iframe { 63 | position: absolute; 64 | top: 0; 65 | left: 0; 66 | width: 100%; 67 | height: 100%; 68 | } 69 | } 70 | } 71 | ` 72 | }) 73 | export class Documentation {} 74 | -------------------------------------------------------------------------------- /src/assets/layout/_topbar.scss: -------------------------------------------------------------------------------- 1 | @use 'mixins' as *; 2 | 3 | .layout-topbar { 4 | position: fixed; 5 | height: 4rem; 6 | z-index: 997; 7 | left: 0; 8 | top: 0; 9 | width: 100%; 10 | padding: 0 2rem; 11 | background-color: var(--surface-card); 12 | transition: left var(--layout-section-transition-duration); 13 | display: flex; 14 | align-items: center; 15 | 16 | .layout-topbar-logo-container { 17 | width: 20rem; 18 | display: flex; 19 | align-items: center; 20 | } 21 | 22 | .layout-topbar-logo { 23 | display: inline-flex; 24 | align-items: center; 25 | font-size: 1.5rem; 26 | border-radius: var(--content-border-radius); 27 | color: var(--text-color); 28 | font-weight: 500; 29 | gap: 0.5rem; 30 | 31 | svg { 32 | width: 3rem; 33 | } 34 | 35 | &:focus-visible { 36 | @include focused(); 37 | } 38 | } 39 | 40 | .layout-topbar-action { 41 | display: inline-flex; 42 | justify-content: center; 43 | align-items: center; 44 | color: var(--text-color-secondary); 45 | border-radius: 50%; 46 | width: 2.5rem; 47 | height: 2.5rem; 48 | color: var(--text-color); 49 | transition: background-color var(--element-transition-duration); 50 | cursor: pointer; 51 | 52 | &:hover { 53 | background-color: var(--surface-hover); 54 | } 55 | 56 | &:focus-visible { 57 | @include focused(); 58 | } 59 | 60 | i { 61 | font-size: 1.25rem; 62 | } 63 | 64 | span { 65 | font-size: 1rem; 66 | display: none; 67 | } 68 | 69 | &.layout-topbar-action-highlight { 70 | background-color: var(--primary-color); 71 | color: var(--primary-contrast-color); 72 | } 73 | } 74 | 75 | .layout-menu-button { 76 | margin-right: 0.5rem; 77 | } 78 | 79 | .layout-topbar-menu-button { 80 | display: none; 81 | } 82 | 83 | .layout-topbar-actions { 84 | margin-left: auto; 85 | display: flex; 86 | gap: 1rem; 87 | } 88 | 89 | .layout-topbar-menu-content { 90 | display: flex; 91 | gap: 1rem; 92 | } 93 | 94 | .layout-config-menu { 95 | display: flex; 96 | gap: 1rem; 97 | } 98 | } 99 | 100 | @media (max-width: 991px) { 101 | .layout-topbar { 102 | padding: 0 2rem; 103 | 104 | .layout-topbar-logo-container { 105 | width: auto; 106 | } 107 | 108 | .layout-menu-button { 109 | margin-left: 0; 110 | margin-right: 0.5rem; 111 | } 112 | 113 | .layout-topbar-menu-button { 114 | display: inline-flex; 115 | } 116 | 117 | .layout-topbar-menu { 118 | position: absolute; 119 | background-color: var(--surface-overlay); 120 | transform-origin: top; 121 | box-shadow: 122 | 0px 3px 5px rgba(0, 0, 0, 0.02), 123 | 0px 0px 2px rgba(0, 0, 0, 0.05), 124 | 0px 1px 4px rgba(0, 0, 0, 0.08); 125 | border-radius: var(--content-border-radius); 126 | padding: 1rem; 127 | right: 2rem; 128 | top: 4rem; 129 | min-width: 15rem; 130 | border: 1px solid var(--surface-border); 131 | 132 | .layout-topbar-menu-content { 133 | gap: 0.5rem; 134 | } 135 | 136 | .layout-topbar-action { 137 | display: flex; 138 | width: 100%; 139 | height: auto; 140 | justify-content: flex-start; 141 | border-radius: var(--content-border-radius); 142 | padding: 0.5rem 1rem; 143 | 144 | i { 145 | font-size: 1rem; 146 | margin-right: 0.5rem; 147 | } 148 | 149 | span { 150 | font-weight: medium; 151 | display: block; 152 | } 153 | } 154 | } 155 | 156 | .layout-topbar-menu-content { 157 | flex-direction: column; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/assets/layout/_menu.scss: -------------------------------------------------------------------------------- 1 | @use 'mixins' as *; 2 | 3 | .layout-sidebar { 4 | position: fixed; 5 | width: 20rem; 6 | height: calc(100vh - 8rem); 7 | z-index: 999; 8 | overflow-y: auto; 9 | user-select: none; 10 | top: 6rem; 11 | left: 2rem; 12 | transition: 13 | transform var(--layout-section-transition-duration), 14 | left var(--layout-section-transition-duration); 15 | background-color: var(--surface-overlay); 16 | border-radius: var(--content-border-radius); 17 | padding: 0.5rem 1.5rem; 18 | } 19 | 20 | .layout-menu { 21 | margin: 0; 22 | padding: 0; 23 | list-style-type: none; 24 | 25 | .layout-root-menuitem { 26 | > .layout-menuitem-root-text { 27 | font-size: 0.857rem; 28 | text-transform: uppercase; 29 | font-weight: 700; 30 | color: var(--text-color); 31 | margin: 0.75rem 0; 32 | } 33 | 34 | > a { 35 | display: none; 36 | } 37 | } 38 | 39 | a { 40 | user-select: none; 41 | 42 | &.active-menuitem { 43 | > .layout-submenu-toggler { 44 | transform: rotate(-180deg); 45 | } 46 | } 47 | } 48 | 49 | li.active-menuitem { 50 | > a { 51 | .layout-submenu-toggler { 52 | transform: rotate(-180deg); 53 | } 54 | } 55 | } 56 | 57 | ul { 58 | margin: 0; 59 | padding: 0; 60 | list-style-type: none; 61 | 62 | a { 63 | display: flex; 64 | align-items: center; 65 | position: relative; 66 | outline: 0 none; 67 | color: var(--text-color); 68 | cursor: pointer; 69 | padding: 0.75rem 1rem; 70 | border-radius: var(--content-border-radius); 71 | transition: 72 | background-color var(--element-transition-duration), 73 | box-shadow var(--element-transition-duration); 74 | 75 | .layout-menuitem-icon { 76 | margin-right: 0.5rem; 77 | } 78 | 79 | .layout-submenu-toggler { 80 | font-size: 75%; 81 | margin-left: auto; 82 | transition: transform var(--element-transition-duration); 83 | } 84 | 85 | &.active-route { 86 | font-weight: 700; 87 | color: var(--primary-color); 88 | } 89 | 90 | &:hover { 91 | background-color: var(--surface-hover); 92 | } 93 | 94 | &:focus { 95 | @include focused-inset(); 96 | } 97 | } 98 | 99 | ul { 100 | overflow: hidden; 101 | border-radius: var(--content-border-radius); 102 | 103 | li { 104 | a { 105 | margin-left: 1rem; 106 | } 107 | 108 | li { 109 | a { 110 | margin-left: 2rem; 111 | } 112 | 113 | li { 114 | a { 115 | margin-left: 2.5rem; 116 | } 117 | 118 | li { 119 | a { 120 | margin-left: 3rem; 121 | } 122 | 123 | li { 124 | a { 125 | margin-left: 3.5rem; 126 | } 127 | 128 | li { 129 | a { 130 | margin-left: 4rem; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | .layout-submenu-enter-from, 143 | .layout-submenu-leave-to { 144 | max-height: 0; 145 | } 146 | 147 | .layout-submenu-enter-to, 148 | .layout-submenu-leave-from { 149 | max-height: 1000px; 150 | } 151 | 152 | .layout-submenu-leave-active { 153 | overflow: hidden; 154 | transition: max-height 0.45s cubic-bezier(0, 1, 0, 1); 155 | } 156 | 157 | .layout-submenu-enter-active { 158 | overflow: hidden; 159 | transition: max-height 1s ease-in-out; 160 | } 161 | -------------------------------------------------------------------------------- /src/app/layout/component/app.layout.ts: -------------------------------------------------------------------------------- 1 | import { Component, Renderer2, ViewChild } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NavigationEnd, Router, RouterModule } from '@angular/router'; 4 | import { filter, Subscription } from 'rxjs'; 5 | import { AppTopbar } from './app.topbar'; 6 | import { AppSidebar } from './app.sidebar'; 7 | import { AppFooter } from './app.footer'; 8 | import { LayoutService } from '../service/layout.service'; 9 | 10 | @Component({ 11 | selector: 'app-layout', 12 | standalone: true, 13 | imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppFooter], 14 | template: `
15 | 16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
` 25 | }) 26 | export class AppLayout { 27 | overlayMenuOpenSubscription: Subscription; 28 | 29 | menuOutsideClickListener: any; 30 | 31 | @ViewChild(AppSidebar) appSidebar!: AppSidebar; 32 | 33 | @ViewChild(AppTopbar) appTopBar!: AppTopbar; 34 | 35 | constructor( 36 | public layoutService: LayoutService, 37 | public renderer: Renderer2, 38 | public router: Router 39 | ) { 40 | this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => { 41 | if (!this.menuOutsideClickListener) { 42 | this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => { 43 | if (this.isOutsideClicked(event)) { 44 | this.hideMenu(); 45 | } 46 | }); 47 | } 48 | 49 | if (this.layoutService.layoutState().staticMenuMobileActive) { 50 | this.blockBodyScroll(); 51 | } 52 | }); 53 | 54 | this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { 55 | this.hideMenu(); 56 | }); 57 | } 58 | 59 | isOutsideClicked(event: MouseEvent) { 60 | const sidebarEl = document.querySelector('.layout-sidebar'); 61 | const topbarEl = document.querySelector('.layout-menu-button'); 62 | const eventTarget = event.target as Node; 63 | 64 | return !(sidebarEl?.isSameNode(eventTarget) || sidebarEl?.contains(eventTarget) || topbarEl?.isSameNode(eventTarget) || topbarEl?.contains(eventTarget)); 65 | } 66 | 67 | hideMenu() { 68 | this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false })); 69 | if (this.menuOutsideClickListener) { 70 | this.menuOutsideClickListener(); 71 | this.menuOutsideClickListener = null; 72 | } 73 | this.unblockBodyScroll(); 74 | } 75 | 76 | blockBodyScroll(): void { 77 | if (document.body.classList) { 78 | document.body.classList.add('blocked-scroll'); 79 | } else { 80 | document.body.className += ' blocked-scroll'; 81 | } 82 | } 83 | 84 | unblockBodyScroll(): void { 85 | if (document.body.classList) { 86 | document.body.classList.remove('blocked-scroll'); 87 | } else { 88 | document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); 89 | } 90 | } 91 | 92 | get containerClass() { 93 | return { 94 | 'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay', 95 | 'layout-static': this.layoutService.layoutConfig().menuMode === 'static', 96 | 'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static', 97 | 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive, 98 | 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive 99 | }; 100 | } 101 | 102 | ngOnDestroy() { 103 | if (this.overlayMenuOpenSubscription) { 104 | this.overlayMenuOpenSubscription.unsubscribe(); 105 | } 106 | 107 | if (this.menuOutsideClickListener) { 108 | this.menuOutsideClickListener(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/components/notificationswidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ButtonModule } from 'primeng/button'; 3 | import { MenuModule } from 'primeng/menu'; 4 | 5 | @Component({ 6 | standalone: true, 7 | selector: 'app-notifications-widget', 8 | imports: [ButtonModule, MenuModule], 9 | template: `
10 |
11 |
Notifications
12 |
13 | 14 | 15 |
16 |
17 | 18 | TODAY 19 |
    20 |
  • 21 |
    22 | 23 |
    24 | Richard Jones 26 | has purchased a blue t-shirt for $79.00 27 | 28 |
  • 29 |
  • 30 |
    31 | 32 |
    33 | Your request for withdrawal of $2500.00 has been initiated. 34 |
  • 35 |
36 | 37 | YESTERDAY 38 |
    39 |
  • 40 |
    41 | 42 |
    43 | Keyser Wick 45 | has purchased a black jacket for $59.00 46 | 47 |
  • 48 |
  • 49 |
    50 | 51 |
    52 | Jane Davis 54 | has posted a new questions about your product. 55 | 56 |
  • 57 |
58 | LAST WEEK 59 |
    60 |
  • 61 |
    62 | 63 |
    64 | Your revenue has increased by %25. 65 |
  • 66 |
  • 67 |
    68 | 69 |
    70 | 12 users have added your products to their wishlist. 71 |
  • 72 |
73 |
` 74 | }) 75 | export class NotificationsWidget { 76 | items = [ 77 | { label: 'Add New', icon: 'pi pi-fw pi-plus' }, 78 | { label: 'Remove', icon: 'pi pi-fw pi-trash' } 79 | ]; 80 | } 81 | -------------------------------------------------------------------------------- /src/app/pages/uikit/mediademo.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { ButtonModule } from 'primeng/button'; 4 | import { CarouselModule } from 'primeng/carousel'; 5 | import { GalleriaModule } from 'primeng/galleria'; 6 | import { ImageModule } from 'primeng/image'; 7 | import { TagModule } from 'primeng/tag'; 8 | import { PhotoService } from '../service/photo.service'; 9 | import { Product, ProductService } from '../service/product.service'; 10 | 11 | @Component({ 12 | selector: 'app-media-demo', 13 | standalone: true, 14 | imports: [CommonModule, CarouselModule, ButtonModule, GalleriaModule, ImageModule, TagModule], 15 | template: `
16 |
Carousel
17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
{{ product.name }}
29 |
30 |
{{ '$' + product.price }}
31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
Image
43 | 44 |
45 | 46 |
47 |
Galleria
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
`, 57 | providers: [ProductService, PhotoService] 58 | }) 59 | export class MediaDemo implements OnInit { 60 | products!: Product[]; 61 | 62 | images!: any[]; 63 | 64 | galleriaResponsiveOptions: any[] = [ 65 | { 66 | breakpoint: '1024px', 67 | numVisible: 5 68 | }, 69 | { 70 | breakpoint: '960px', 71 | numVisible: 4 72 | }, 73 | { 74 | breakpoint: '768px', 75 | numVisible: 3 76 | }, 77 | { 78 | breakpoint: '560px', 79 | numVisible: 1 80 | } 81 | ]; 82 | 83 | carouselResponsiveOptions: any[] = [ 84 | { 85 | breakpoint: '1024px', 86 | numVisible: 3, 87 | numScroll: 3 88 | }, 89 | { 90 | breakpoint: '768px', 91 | numVisible: 2, 92 | numScroll: 2 93 | }, 94 | { 95 | breakpoint: '560px', 96 | numVisible: 1, 97 | numScroll: 1 98 | } 99 | ]; 100 | 101 | constructor( 102 | private productService: ProductService, 103 | private photoService: PhotoService 104 | ) {} 105 | 106 | ngOnInit() { 107 | this.productService.getProductsSmall().then((products) => { 108 | this.products = products; 109 | }); 110 | 111 | this.photoService.getImages().then((images) => { 112 | this.images = images; 113 | }); 114 | } 115 | 116 | getSeverity(status: string) { 117 | switch (status) { 118 | case 'INSTOCK': 119 | return 'success'; 120 | case 'LOWSTOCK': 121 | return 'warn'; 122 | case 'OUTOFSTOCK': 123 | return 'danger'; 124 | default: 125 | return 'success'; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/app/pages/service/photo.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class PhotoService { 5 | getData() { 6 | return [ 7 | { 8 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria1.jpg', 9 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria1s.jpg', 10 | alt: 'Description for Image 1', 11 | title: 'Title 1' 12 | }, 13 | { 14 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria2.jpg', 15 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria2s.jpg', 16 | alt: 'Description for Image 2', 17 | title: 'Title 2' 18 | }, 19 | { 20 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria3.jpg', 21 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria3s.jpg', 22 | alt: 'Description for Image 3', 23 | title: 'Title 3' 24 | }, 25 | { 26 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria4.jpg', 27 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria4s.jpg', 28 | alt: 'Description for Image 4', 29 | title: 'Title 4' 30 | }, 31 | { 32 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria5.jpg', 33 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria5s.jpg', 34 | alt: 'Description for Image 5', 35 | title: 'Title 5' 36 | }, 37 | { 38 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria6.jpg', 39 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria6s.jpg', 40 | alt: 'Description for Image 6', 41 | title: 'Title 6' 42 | }, 43 | { 44 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria7.jpg', 45 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria7s.jpg', 46 | alt: 'Description for Image 7', 47 | title: 'Title 7' 48 | }, 49 | { 50 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria8.jpg', 51 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria8s.jpg', 52 | alt: 'Description for Image 8', 53 | title: 'Title 8' 54 | }, 55 | { 56 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria9.jpg', 57 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria9s.jpg', 58 | alt: 'Description for Image 9', 59 | title: 'Title 9' 60 | }, 61 | { 62 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria10.jpg', 63 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria10s.jpg', 64 | alt: 'Description for Image 10', 65 | title: 'Title 10' 66 | }, 67 | { 68 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria11.jpg', 69 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria11s.jpg', 70 | alt: 'Description for Image 11', 71 | title: 'Title 11' 72 | }, 73 | { 74 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria12.jpg', 75 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria12s.jpg', 76 | alt: 'Description for Image 12', 77 | title: 'Title 12' 78 | }, 79 | { 80 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria13.jpg', 81 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria13s.jpg', 82 | alt: 'Description for Image 13', 83 | title: 'Title 13' 84 | }, 85 | { 86 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria14.jpg', 87 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria14s.jpg', 88 | alt: 'Description for Image 14', 89 | title: 'Title 14' 90 | }, 91 | { 92 | itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria15.jpg', 93 | thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria15s.jpg', 94 | alt: 'Description for Image 15', 95 | title: 'Title 15' 96 | } 97 | ]; 98 | } 99 | 100 | getImages() { 101 | return Promise.resolve(this.getData()); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/app/layout/service/layout.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, effect, signal, computed } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | export interface layoutConfig { 5 | preset?: string; 6 | primary?: string; 7 | surface?: string | undefined | null; 8 | darkTheme?: boolean; 9 | menuMode?: string; 10 | } 11 | 12 | interface LayoutState { 13 | staticMenuDesktopInactive?: boolean; 14 | overlayMenuActive?: boolean; 15 | configSidebarVisible?: boolean; 16 | staticMenuMobileActive?: boolean; 17 | menuHoverActive?: boolean; 18 | } 19 | 20 | interface MenuChangeEvent { 21 | key: string; 22 | routeEvent?: boolean; 23 | } 24 | 25 | @Injectable({ 26 | providedIn: 'root' 27 | }) 28 | export class LayoutService { 29 | _config: layoutConfig = { 30 | preset: 'Aura', 31 | primary: 'emerald', 32 | surface: null, 33 | darkTheme: false, 34 | menuMode: 'static' 35 | }; 36 | 37 | _state: LayoutState = { 38 | staticMenuDesktopInactive: false, 39 | overlayMenuActive: false, 40 | configSidebarVisible: false, 41 | staticMenuMobileActive: false, 42 | menuHoverActive: false 43 | }; 44 | 45 | layoutConfig = signal(this._config); 46 | 47 | layoutState = signal(this._state); 48 | 49 | private configUpdate = new Subject(); 50 | 51 | private overlayOpen = new Subject(); 52 | 53 | private menuSource = new Subject(); 54 | 55 | private resetSource = new Subject(); 56 | 57 | menuSource$ = this.menuSource.asObservable(); 58 | 59 | resetSource$ = this.resetSource.asObservable(); 60 | 61 | configUpdate$ = this.configUpdate.asObservable(); 62 | 63 | overlayOpen$ = this.overlayOpen.asObservable(); 64 | 65 | theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark')); 66 | 67 | isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive); 68 | 69 | isDarkTheme = computed(() => this.layoutConfig().darkTheme); 70 | 71 | getPrimary = computed(() => this.layoutConfig().primary); 72 | 73 | getSurface = computed(() => this.layoutConfig().surface); 74 | 75 | isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay'); 76 | 77 | transitionComplete = signal(false); 78 | 79 | private initialized = false; 80 | 81 | constructor() { 82 | effect(() => { 83 | const config = this.layoutConfig(); 84 | if (config) { 85 | this.onConfigUpdate(); 86 | } 87 | }); 88 | 89 | effect(() => { 90 | const config = this.layoutConfig(); 91 | 92 | if (!this.initialized || !config) { 93 | this.initialized = true; 94 | return; 95 | } 96 | 97 | this.handleDarkModeTransition(config); 98 | }); 99 | } 100 | 101 | private handleDarkModeTransition(config: layoutConfig): void { 102 | if ((document as any).startViewTransition) { 103 | this.startViewTransition(config); 104 | } else { 105 | this.toggleDarkMode(config); 106 | this.onTransitionEnd(); 107 | } 108 | } 109 | 110 | private startViewTransition(config: layoutConfig): void { 111 | const transition = (document as any).startViewTransition(() => { 112 | this.toggleDarkMode(config); 113 | }); 114 | 115 | transition.ready 116 | .then(() => { 117 | this.onTransitionEnd(); 118 | }) 119 | .catch(() => {}); 120 | } 121 | 122 | toggleDarkMode(config?: layoutConfig): void { 123 | const _config = config || this.layoutConfig(); 124 | if (_config.darkTheme) { 125 | document.documentElement.classList.add('app-dark'); 126 | } else { 127 | document.documentElement.classList.remove('app-dark'); 128 | } 129 | } 130 | 131 | private onTransitionEnd() { 132 | this.transitionComplete.set(true); 133 | setTimeout(() => { 134 | this.transitionComplete.set(false); 135 | }); 136 | } 137 | 138 | onMenuToggle() { 139 | if (this.isOverlay()) { 140 | this.layoutState.update((prev) => ({ ...prev, overlayMenuActive: !this.layoutState().overlayMenuActive })); 141 | 142 | if (this.layoutState().overlayMenuActive) { 143 | this.overlayOpen.next(null); 144 | } 145 | } 146 | 147 | if (this.isDesktop()) { 148 | this.layoutState.update((prev) => ({ ...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive })); 149 | } else { 150 | this.layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive })); 151 | 152 | if (this.layoutState().staticMenuMobileActive) { 153 | this.overlayOpen.next(null); 154 | } 155 | } 156 | } 157 | 158 | isDesktop() { 159 | return window.innerWidth > 991; 160 | } 161 | 162 | isMobile() { 163 | return !this.isDesktop(); 164 | } 165 | 166 | onConfigUpdate() { 167 | this._config = { ...this.layoutConfig() }; 168 | this.configUpdate.next(this.layoutConfig()); 169 | } 170 | 171 | onMenuStateChange(event: MenuChangeEvent) { 172 | this.menuSource.next(event); 173 | } 174 | 175 | reset() { 176 | this.resetSource.next(true); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/app/pages/dashboard/components/bestsellingwidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ButtonModule } from 'primeng/button'; 4 | import { MenuModule } from 'primeng/menu'; 5 | 6 | @Component({ 7 | standalone: true, 8 | selector: 'app-best-selling-widget', 9 | imports: [CommonModule, ButtonModule, MenuModule], 10 | template: `
11 |
12 |
Best Selling Products
13 |
14 | 15 | 16 |
17 |
18 |
    19 |
  • 20 |
    21 | Space T-Shirt 22 |
    Clothing
    23 |
    24 |
    25 |
    26 |
    27 |
    28 | %50 29 |
    30 |
  • 31 |
  • 32 |
    33 | Portal Sticker 34 |
    Accessories
    35 |
    36 |
    37 |
    38 |
    39 |
    40 | %16 41 |
    42 |
  • 43 |
  • 44 |
    45 | Supernova Sticker 46 |
    Accessories
    47 |
    48 |
    49 |
    50 |
    51 |
    52 | %67 53 |
    54 |
  • 55 |
  • 56 |
    57 | Wonders Notebook 58 |
    Office
    59 |
    60 |
    61 |
    62 |
    63 |
    64 | %35 65 |
    66 |
  • 67 |
  • 68 |
    69 | Mat Black Case 70 |
    Accessories
    71 |
    72 |
    73 |
    74 |
    75 |
    76 | %75 77 |
    78 |
  • 79 |
  • 80 |
    81 | Robots T-Shirt 82 |
    Clothing
    83 |
    84 |
    85 |
    86 |
    87 |
    88 | %40 89 |
    90 |
  • 91 |
92 |
` 93 | }) 94 | export class BestSellingWidget { 95 | menu = null; 96 | 97 | items = [ 98 | { label: 'Add New', icon: 'pi pi-fw pi-plus' }, 99 | { label: 'Remove', icon: 'pi pi-fw pi-trash' } 100 | ]; 101 | } 102 | -------------------------------------------------------------------------------- /src/app/pages/uikit/timelinedemo.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {TimelineModule} from 'primeng/timeline'; 3 | import {CardModule} from 'primeng/card'; 4 | import {CommonModule} from '@angular/common'; 5 | import {ButtonModule} from 'primeng/button'; 6 | 7 | @Component({ 8 | selector: 'app-timeline-demo', 9 | standalone: true, 10 | imports: [CommonModule, TimelineModule, ButtonModule, CardModule], 11 | template: `
12 |
13 |
14 |
Left Align
15 | 16 | 17 | {{ event.status }} 18 | 19 | 20 |
21 |
22 |
23 |
24 |
Right Align
25 | 26 | 27 | {{ event.status }} 28 | 29 | 30 |
31 |
32 |
33 |
34 |
Alternate Align
35 | 36 | 37 | {{ event.status }} 38 | 39 | 40 |
41 |
42 |
43 |
44 |
Opposite Content
45 | 46 | 47 | {{ event.date }} 48 | 49 | 50 | {{ event.status }} 51 | 52 | 53 |
54 |
55 |
56 |
57 |
Templating
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |

68 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, 69 | cupiditate neque quas! 70 |

71 | 72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
Horizontal
80 |
Top Align
81 | 82 | 83 | {{ event }} 84 | 85 | 86 | 87 |
Bottom Align
88 | 89 | 90 | {{ event }} 91 | 92 | 93 | 94 |
Alternate Align
95 | 96 | 97 | {{ event }} 98 | 99 |   100 | 101 |
102 |
103 |
` 104 | }) 105 | export class TimelineDemo { 106 | events1: any[] = []; 107 | 108 | events2: any[] = []; 109 | 110 | ngOnInit() { 111 | this.events1 = [ 112 | { 113 | status: 'Ordered', 114 | date: '15/10/2020 10:30', 115 | icon: 'pi pi-shopping-cart', 116 | color: '#9C27B0', 117 | image: 'game-controller.jpg' 118 | }, 119 | { 120 | status: 'Processing', 121 | date: '15/10/2020 14:00', 122 | icon: 'pi pi-cog', 123 | color: '#673AB7' 124 | }, 125 | { 126 | status: 'Shipped', 127 | date: '15/10/2020 16:15', 128 | icon: 'pi pi-envelope', 129 | color: '#FF9800' 130 | }, 131 | { 132 | status: 'Delivered', 133 | date: '16/10/2020 10:00', 134 | icon: 'pi pi-check', 135 | color: '#607D8B' 136 | } 137 | ]; 138 | 139 | this.events2 = ['2020', '2021', '2022', '2023']; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/app/pages/uikit/formlayoutdemo.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FluidModule } from 'primeng/fluid'; 3 | import { InputTextModule } from 'primeng/inputtext'; 4 | import { ButtonModule } from 'primeng/button'; 5 | import { SelectModule } from 'primeng/select'; 6 | import { FormsModule } from '@angular/forms'; 7 | import { TextareaModule } from 'primeng/textarea'; 8 | 9 | @Component({ 10 | selector: 'app-formlayout-demo', 11 | standalone: true, 12 | imports: [InputTextModule, FluidModule, ButtonModule, SelectModule, FormsModule, TextareaModule], 13 | template: ` 14 |
15 |
16 |
17 |
Vertical
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 |
33 |
Vertical Grid
34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
Horizontal
49 |
50 | 51 |
52 | 53 |
54 |
55 |
56 | 57 |
58 | 59 |
60 |
61 |
62 | 63 |
64 |
Inline
65 |
66 |
67 | 68 | 69 |
70 |
71 | 72 | 73 |
74 | 75 |
76 |
77 |
78 |
Help Text
79 |
80 | 81 | 82 | Enter your username to reset your password. 83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 |
Advanced
91 |
92 |
93 | 94 | 95 |
96 |
97 | 98 | 99 |
100 |
101 | 102 |
103 | 104 | 105 |
106 | 107 |
108 |
109 | 110 | 111 |
112 |
113 | 114 | 115 |
116 |
117 |
118 |
119 |
` 120 | }) 121 | export class FormLayoutDemo { 122 | dropdownItems = [ 123 | { name: 'Option 1', code: 'Option 1' }, 124 | { name: 'Option 2', code: 'Option 2' }, 125 | { name: 'Option 3', code: 'Option 3' } 126 | ]; 127 | 128 | dropdownItem = null; 129 | } 130 | -------------------------------------------------------------------------------- /src/app/layout/component/app.menuitem.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostBinding, Input } from '@angular/core'; 2 | import { NavigationEnd, Router, RouterModule } from '@angular/router'; 3 | import { animate, state, style, transition, trigger } from '@angular/animations'; 4 | import { Subscription } from 'rxjs'; 5 | import { filter } from 'rxjs/operators'; 6 | import { CommonModule } from '@angular/common'; 7 | import { RippleModule } from 'primeng/ripple'; 8 | import { MenuItem } from 'primeng/api'; 9 | import { LayoutService } from '../service/layout.service'; 10 | 11 | @Component({ 12 | // eslint-disable-next-line @angular-eslint/component-selector 13 | selector: '[app-menuitem]', 14 | imports: [CommonModule, RouterModule, RippleModule], 15 | template: ` 16 | 17 |
{{ item.label }}
18 | 19 | 20 | {{ item.label }} 21 | 22 | 23 | 41 | 42 | {{ item.label }} 43 | 44 | 45 | 46 |
    47 | 48 |
  • 49 |
    50 |
51 |
52 | `, 53 | animations: [ 54 | trigger('children', [ 55 | state( 56 | 'collapsed', 57 | style({ 58 | height: '0' 59 | }) 60 | ), 61 | state( 62 | 'expanded', 63 | style({ 64 | height: '*' 65 | }) 66 | ), 67 | transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')) 68 | ]) 69 | ], 70 | providers: [LayoutService] 71 | }) 72 | export class AppMenuitem { 73 | @Input() item!: MenuItem; 74 | 75 | @Input() index!: number; 76 | 77 | @Input() @HostBinding('class.layout-root-menuitem') root!: boolean; 78 | 79 | @Input() parentKey!: string; 80 | 81 | active = false; 82 | 83 | menuSourceSubscription: Subscription; 84 | 85 | menuResetSubscription: Subscription; 86 | 87 | key: string = ''; 88 | 89 | constructor( 90 | public router: Router, 91 | private layoutService: LayoutService 92 | ) { 93 | this.menuSourceSubscription = this.layoutService.menuSource$.subscribe((value) => { 94 | Promise.resolve(null).then(() => { 95 | if (value.routeEvent) { 96 | this.active = value.key === this.key || value.key.startsWith(this.key + '-') ? true : false; 97 | } else { 98 | if (value.key !== this.key && !value.key.startsWith(this.key + '-')) { 99 | this.active = false; 100 | } 101 | } 102 | }); 103 | }); 104 | 105 | this.menuResetSubscription = this.layoutService.resetSource$.subscribe(() => { 106 | this.active = false; 107 | }); 108 | 109 | this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((params) => { 110 | if (this.item.routerLink) { 111 | this.updateActiveStateFromRoute(); 112 | } 113 | }); 114 | } 115 | 116 | ngOnInit() { 117 | this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index); 118 | 119 | if (this.item.routerLink) { 120 | this.updateActiveStateFromRoute(); 121 | } 122 | } 123 | 124 | updateActiveStateFromRoute() { 125 | let activeRoute = this.router.isActive(this.item.routerLink[0], { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }); 126 | 127 | if (activeRoute) { 128 | this.layoutService.onMenuStateChange({ key: this.key, routeEvent: true }); 129 | } 130 | } 131 | 132 | itemClick(event: Event) { 133 | // avoid processing disabled items 134 | if (this.item.disabled) { 135 | event.preventDefault(); 136 | return; 137 | } 138 | 139 | // execute command 140 | if (this.item.command) { 141 | this.item.command({ originalEvent: event, item: this.item }); 142 | } 143 | 144 | // toggle active state 145 | if (this.item.items) { 146 | this.active = !this.active; 147 | } 148 | 149 | this.layoutService.onMenuStateChange({ key: this.key }); 150 | } 151 | 152 | get submenuAnimation() { 153 | return this.root ? 'expanded' : this.active ? 'expanded' : 'collapsed'; 154 | } 155 | 156 | @HostBinding('class.active-menuitem') 157 | get activeClass() { 158 | return this.active && !this.root; 159 | } 160 | 161 | ngOnDestroy() { 162 | if (this.menuSourceSubscription) { 163 | this.menuSourceSubscription.unsubscribe(); 164 | } 165 | 166 | if (this.menuResetSubscription) { 167 | this.menuResetSubscription.unsubscribe(); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/app/layout/component/app.menu.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { MenuItem } from 'primeng/api'; 5 | import { AppMenuitem } from './app.menuitem'; 6 | 7 | @Component({ 8 | selector: 'app-menu', 9 | standalone: true, 10 | imports: [CommonModule, AppMenuitem, RouterModule], 11 | template: `
    12 | 13 |
  • 14 | 15 |
    16 |
` 17 | }) 18 | export class AppMenu { 19 | model: MenuItem[] = []; 20 | 21 | ngOnInit() { 22 | this.model = [ 23 | { 24 | label: 'Home', 25 | items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] }] 26 | }, 27 | { 28 | label: 'UI Components', 29 | items: [ 30 | { label: 'Form Layout', icon: 'pi pi-fw pi-id-card', routerLink: ['/uikit/formlayout'] }, 31 | { label: 'Input', icon: 'pi pi-fw pi-check-square', routerLink: ['/uikit/input'] }, 32 | { label: 'Button', icon: 'pi pi-fw pi-mobile', class: 'rotated-icon', routerLink: ['/uikit/button'] }, 33 | { label: 'Table', icon: 'pi pi-fw pi-table', routerLink: ['/uikit/table'] }, 34 | { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'] }, 35 | { label: 'Tree', icon: 'pi pi-fw pi-share-alt', routerLink: ['/uikit/tree'] }, 36 | { label: 'Panel', icon: 'pi pi-fw pi-tablet', routerLink: ['/uikit/panel'] }, 37 | { label: 'Overlay', icon: 'pi pi-fw pi-clone', routerLink: ['/uikit/overlay'] }, 38 | { label: 'Media', icon: 'pi pi-fw pi-image', routerLink: ['/uikit/media'] }, 39 | { label: 'Menu', icon: 'pi pi-fw pi-bars', routerLink: ['/uikit/menu'] }, 40 | { label: 'Message', icon: 'pi pi-fw pi-comment', routerLink: ['/uikit/message'] }, 41 | { label: 'File', icon: 'pi pi-fw pi-file', routerLink: ['/uikit/file'] }, 42 | { label: 'Chart', icon: 'pi pi-fw pi-chart-bar', routerLink: ['/uikit/charts'] }, 43 | { label: 'Timeline', icon: 'pi pi-fw pi-calendar', routerLink: ['/uikit/timeline'] }, 44 | { label: 'Misc', icon: 'pi pi-fw pi-circle', routerLink: ['/uikit/misc'] } 45 | ] 46 | }, 47 | { 48 | label: 'Pages', 49 | icon: 'pi pi-fw pi-briefcase', 50 | routerLink: ['/pages'], 51 | items: [ 52 | { 53 | label: 'Landing', 54 | icon: 'pi pi-fw pi-globe', 55 | routerLink: ['/landing'] 56 | }, 57 | { 58 | label: 'Auth', 59 | icon: 'pi pi-fw pi-user', 60 | items: [ 61 | { 62 | label: 'Login', 63 | icon: 'pi pi-fw pi-sign-in', 64 | routerLink: ['/auth/login'] 65 | }, 66 | { 67 | label: 'Error', 68 | icon: 'pi pi-fw pi-times-circle', 69 | routerLink: ['/auth/error'] 70 | }, 71 | { 72 | label: 'Access Denied', 73 | icon: 'pi pi-fw pi-lock', 74 | routerLink: ['/auth/access'] 75 | } 76 | ] 77 | }, 78 | { 79 | label: 'Crud', 80 | icon: 'pi pi-fw pi-pencil', 81 | routerLink: ['/pages/crud'] 82 | }, 83 | { 84 | label: 'Not Found', 85 | icon: 'pi pi-fw pi-exclamation-circle', 86 | routerLink: ['/pages/notfound'] 87 | }, 88 | { 89 | label: 'Empty', 90 | icon: 'pi pi-fw pi-circle-off', 91 | routerLink: ['/pages/empty'] 92 | } 93 | ] 94 | }, 95 | { 96 | label: 'Hierarchy', 97 | items: [ 98 | { 99 | label: 'Submenu 1', 100 | icon: 'pi pi-fw pi-bookmark', 101 | items: [ 102 | { 103 | label: 'Submenu 1.1', 104 | icon: 'pi pi-fw pi-bookmark', 105 | items: [ 106 | { label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' }, 107 | { label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' }, 108 | { label: 'Submenu 1.1.3', icon: 'pi pi-fw pi-bookmark' } 109 | ] 110 | }, 111 | { 112 | label: 'Submenu 1.2', 113 | icon: 'pi pi-fw pi-bookmark', 114 | items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }] 115 | } 116 | ] 117 | }, 118 | { 119 | label: 'Submenu 2', 120 | icon: 'pi pi-fw pi-bookmark', 121 | items: [ 122 | { 123 | label: 'Submenu 2.1', 124 | icon: 'pi pi-fw pi-bookmark', 125 | items: [ 126 | { label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' }, 127 | { label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' } 128 | ] 129 | }, 130 | { 131 | label: 'Submenu 2.2', 132 | icon: 'pi pi-fw pi-bookmark', 133 | items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }] 134 | } 135 | ] 136 | } 137 | ] 138 | }, 139 | { 140 | label: 'Get Started', 141 | items: [ 142 | { 143 | label: 'Documentation', 144 | icon: 'pi pi-fw pi-book', 145 | routerLink: ['/documentation'] 146 | }, 147 | { 148 | label: 'View Source', 149 | icon: 'pi pi-fw pi-github', 150 | url: 'https://github.com/primefaces/sakai-ng', 151 | target: '_blank' 152 | } 153 | ] 154 | } 155 | ]; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/pricingwidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ButtonModule } from 'primeng/button'; 3 | import { DividerModule } from 'primeng/divider'; 4 | import { RippleModule } from 'primeng/ripple'; 5 | 6 | @Component({ 7 | selector: 'pricing-widget', 8 | imports: [DividerModule, ButtonModule, RippleModule], 9 | template: ` 10 |
11 |
12 |
Matchless Pricing
13 | Amet consectetur adipiscing elit... 14 |
15 | 16 |
17 |
18 |
19 |
Free
20 | free 21 |
22 |
23 | $0 24 | per month 25 |
26 | 27 |
28 | 29 |
    30 |
  • 31 | 32 | Responsive Layout 33 |
  • 34 |
  • 35 | 36 | Unlimited Push Messages 37 |
  • 38 |
  • 39 | 40 | 50 Support Ticket 41 |
  • 42 |
  • 43 | 44 | Free Shipping 45 |
  • 46 |
47 |
48 |
49 | 50 |
51 |
52 |
Startup
53 | startup 54 |
55 |
56 | $1 57 | per month 58 |
59 | 60 |
61 | 62 |
    63 |
  • 64 | 65 | Responsive Layout 66 |
  • 67 |
  • 68 | 69 | Unlimited Push Messages 70 |
  • 71 |
  • 72 | 73 | 50 Support Ticket 74 |
  • 75 |
  • 76 | 77 | Free Shipping 78 |
  • 79 |
80 |
81 |
82 | 83 |
84 |
85 |
Enterprise
86 | enterprise 87 |
88 |
89 | $5 90 | per month 91 |
92 | 93 |
94 | 95 |
    96 |
  • 97 | 98 | Responsive Layout 99 |
  • 100 |
  • 101 | 102 | Unlimited Push Messages 103 |
  • 104 |
  • 105 | 106 | 50 Support Ticket 107 |
  • 108 |
  • 109 | 110 | Free Shipping 111 |
  • 112 |
113 |
114 |
115 |
116 |
117 | ` 118 | }) 119 | export class PricingWidget {} 120 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/topbarwidget.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { StyleClassModule } from 'primeng/styleclass'; 3 | import { Router, RouterModule } from '@angular/router'; 4 | import { RippleModule } from 'primeng/ripple'; 5 | import { ButtonModule } from 'primeng/button'; 6 | import {AppFloatingConfigurator} from "@/layout/component/app.floatingconfigurator"; 7 | 8 | @Component({ 9 | selector: 'topbar-widget', 10 | imports: [RouterModule, StyleClassModule, ButtonModule, RippleModule, AppFloatingConfigurator], 11 | template: ` 12 | 13 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | SAKAI 30 | 31 | 32 | 33 | 34 | 35 | 36 | ` 65 | }) 66 | export class TopbarWidget { 67 | constructor(public router: Router) {} 68 | } 69 | -------------------------------------------------------------------------------- /src/assets/demo/flags/flags.scss: -------------------------------------------------------------------------------- 1 | span.flag{width:44px;height:30px;display:inline-block;}img.flag{width:30px}.flag{background:url(./flags_responsive.png) no-repeat;background-size:100%;vertical-align: middle;}.flag-ad{background-position:0 .413223%}.flag-ae{background-position:0 .826446%}.flag-af{background-position:0 1.239669%}.flag-ag{background-position:0 1.652893%}.flag-ai{background-position:0 2.066116%}.flag-al{background-position:0 2.479339%}.flag-am{background-position:0 2.892562%}.flag-an{background-position:0 3.305785%}.flag-ao{background-position:0 3.719008%}.flag-aq{background-position:0 4.132231%}.flag-ar{background-position:0 4.545455%}.flag-as{background-position:0 4.958678%}.flag-at{background-position:0 5.371901%}.flag-au{background-position:0 5.785124%}.flag-aw{background-position:0 6.198347%}.flag-az{background-position:0 6.61157%}.flag-ba{background-position:0 7.024793%}.flag-bb{background-position:0 7.438017%}.flag-bd{background-position:0 7.85124%}.flag-be{background-position:0 8.264463%}.flag-bf{background-position:0 8.677686%}.flag-bg{background-position:0 9.090909%}.flag-bh{background-position:0 9.504132%}.flag-bi{background-position:0 9.917355%}.flag-bj{background-position:0 10.330579%}.flag-bm{background-position:0 10.743802%}.flag-bn{background-position:0 11.157025%}.flag-bo{background-position:0 11.570248%}.flag-br{background-position:0 11.983471%}.flag-bs{background-position:0 12.396694%}.flag-bt{background-position:0 12.809917%}.flag-bv{background-position:0 13.22314%}.flag-bw{background-position:0 13.636364%}.flag-by{background-position:0 14.049587%}.flag-bz{background-position:0 14.46281%}.flag-ca{background-position:0 14.876033%}.flag-cc{background-position:0 15.289256%}.flag-cd{background-position:0 15.702479%}.flag-cf{background-position:0 16.115702%}.flag-cg{background-position:0 16.528926%}.flag-ch{background-position:0 16.942149%}.flag-ci{background-position:0 17.355372%}.flag-ck{background-position:0 17.768595%}.flag-cl{background-position:0 18.181818%}.flag-cm{background-position:0 18.595041%}.flag-cn{background-position:0 19.008264%}.flag-co{background-position:0 19.421488%}.flag-cr{background-position:0 19.834711%}.flag-cu{background-position:0 20.247934%}.flag-cv{background-position:0 20.661157%}.flag-cx{background-position:0 21.07438%}.flag-cy{background-position:0 21.487603%}.flag-cz{background-position:0 21.900826%}.flag-de{background-position:0 22.31405%}.flag-dj{background-position:0 22.727273%}.flag-dk{background-position:0 23.140496%}.flag-dm{background-position:0 23.553719%}.flag-do{background-position:0 23.966942%}.flag-dz{background-position:0 24.380165%}.flag-ec{background-position:0 24.793388%}.flag-ee{background-position:0 25.206612%}.flag-eg{background-position:0 25.619835%}.flag-eh{background-position:0 26.033058%}.flag-er{background-position:0 26.446281%}.flag-es{background-position:0 26.859504%}.flag-et{background-position:0 27.272727%}.flag-fi{background-position:0 27.68595%}.flag-fj{background-position:0 28.099174%}.flag-fk{background-position:0 28.512397%}.flag-fm{background-position:0 28.92562%}.flag-fo{background-position:0 29.338843%}.flag-fr{background-position:0 29.752066%}.flag-ga{background-position:0 30.165289%}.flag-gd{background-position:0 30.578512%}.flag-ge{background-position:0 30.991736%}.flag-gf{background-position:0 31.404959%}.flag-gh{background-position:0 31.818182%}.flag-gi{background-position:0 32.231405%}.flag-gl{background-position:0 32.644628%}.flag-gm{background-position:0 33.057851%}.flag-gn{background-position:0 33.471074%}.flag-gp{background-position:0 33.884298%}.flag-gq{background-position:0 34.297521%}.flag-gr{background-position:0 34.710744%}.flag-gs{background-position:0 35.123967%}.flag-gt{background-position:0 35.53719%}.flag-gu{background-position:0 35.950413%}.flag-gw{background-position:0 36.363636%}.flag-gy{background-position:0 36.77686%}.flag-hk{background-position:0 37.190083%}.flag-hm{background-position:0 37.603306%}.flag-hn{background-position:0 38.016529%}.flag-hr{background-position:0 38.429752%}.flag-ht{background-position:0 38.842975%}.flag-hu{background-position:0 39.256198%}.flag-id{background-position:0 39.669421%}.flag-ie{background-position:0 40.082645%}.flag-il{background-position:0 40.495868%}.flag-in{background-position:0 40.909091%}.flag-io{background-position:0 41.322314%}.flag-iq{background-position:0 41.735537%}.flag-ir{background-position:0 42.14876%}.flag-is{background-position:0 42.561983%}.flag-it{background-position:0 42.975207%}.flag-jm{background-position:0 43.38843%}.flag-jo{background-position:0 43.801653%}.flag-jp{background-position:0 44.214876%}.flag-ke{background-position:0 44.628099%}.flag-kg{background-position:0 45.041322%}.flag-kh{background-position:0 45.454545%}.flag-ki{background-position:0 45.867769%}.flag-km{background-position:0 46.280992%}.flag-kn{background-position:0 46.694215%}.flag-kp{background-position:0 47.107438%}.flag-kr{background-position:0 47.520661%}.flag-kw{background-position:0 47.933884%}.flag-ky{background-position:0 48.347107%}.flag-kz{background-position:0 48.760331%}.flag-la{background-position:0 49.173554%}.flag-lb{background-position:0 49.586777%}.flag-lc{background-position:0 50%}.flag-li{background-position:0 50.413223%}.flag-lk{background-position:0 50.826446%}.flag-lr{background-position:0 51.239669%}.flag-ls{background-position:0 51.652893%}.flag-lt{background-position:0 52.066116%}.flag-lu{background-position:0 52.479339%}.flag-lv{background-position:0 52.892562%}.flag-ly{background-position:0 53.305785%}.flag-ma{background-position:0 53.719008%}.flag-mc{background-position:0 54.132231%}.flag-md{background-position:0 54.545455%}.flag-me{background-position:0 54.958678%}.flag-mg{background-position:0 55.371901%}.flag-mh{background-position:0 55.785124%}.flag-mk{background-position:0 56.198347%}.flag-ml{background-position:0 56.61157%}.flag-mm{background-position:0 57.024793%}.flag-mn{background-position:0 57.438017%}.flag-mo{background-position:0 57.85124%}.flag-mp{background-position:0 58.264463%}.flag-mq{background-position:0 58.677686%}.flag-mr{background-position:0 59.090909%}.flag-ms{background-position:0 59.504132%}.flag-mt{background-position:0 59.917355%}.flag-mu{background-position:0 60.330579%}.flag-mv{background-position:0 60.743802%}.flag-mw{background-position:0 61.157025%}.flag-mx{background-position:0 61.570248%}.flag-my{background-position:0 61.983471%}.flag-mz{background-position:0 62.396694%}.flag-na{background-position:0 62.809917%}.flag-nc{background-position:0 63.22314%}.flag-ne{background-position:0 63.636364%}.flag-nf{background-position:0 64.049587%}.flag-ng{background-position:0 64.46281%}.flag-ni{background-position:0 64.876033%}.flag-nl{background-position:0 65.289256%}.flag-no{background-position:0 65.702479%}.flag-np{background-position:0 66.115702%}.flag-nr{background-position:0 66.528926%}.flag-nu{background-position:0 66.942149%}.flag-nz{background-position:0 67.355372%}.flag-om{background-position:0 67.768595%}.flag-pa{background-position:0 68.181818%}.flag-pe{background-position:0 68.595041%}.flag-pf{background-position:0 69.008264%}.flag-pg{background-position:0 69.421488%}.flag-ph{background-position:0 69.834711%}.flag-pk{background-position:0 70.247934%}.flag-pl{background-position:0 70.661157%}.flag-pm{background-position:0 71.07438%}.flag-pn{background-position:0 71.487603%}.flag-pr{background-position:0 71.900826%}.flag-pt{background-position:0 72.31405%}.flag-pw{background-position:0 72.727273%}.flag-py{background-position:0 73.140496%}.flag-qa{background-position:0 73.553719%}.flag-re{background-position:0 73.966942%}.flag-ro{background-position:0 74.380165%}.flag-rs{background-position:0 74.793388%}.flag-ru{background-position:0 75.206612%}.flag-rw{background-position:0 75.619835%}.flag-sa{background-position:0 76.033058%}.flag-sb{background-position:0 76.446281%}.flag-sc{background-position:0 76.859504%}.flag-sd{background-position:0 77.272727%}.flag-se{background-position:0 77.68595%}.flag-sg{background-position:0 78.099174%}.flag-sh{background-position:0 78.512397%}.flag-si{background-position:0 78.92562%}.flag-sj{background-position:0 79.338843%}.flag-sk{background-position:0 79.752066%}.flag-sl{background-position:0 80.165289%}.flag-sm{background-position:0 80.578512%}.flag-sn{background-position:0 80.991736%}.flag-so{background-position:0 81.404959%}.flag-sr{background-position:0 81.818182%}.flag-ss{background-position:0 82.231405%}.flag-st{background-position:0 82.644628%}.flag-sv{background-position:0 83.057851%}.flag-sy{background-position:0 83.471074%}.flag-sz{background-position:0 83.884298%}.flag-tc{background-position:0 84.297521%}.flag-td{background-position:0 84.710744%}.flag-tf{background-position:0 85.123967%}.flag-tg{background-position:0 85.53719%}.flag-th{background-position:0 85.950413%}.flag-tj{background-position:0 86.363636%}.flag-tk{background-position:0 86.77686%}.flag-tl{background-position:0 87.190083%}.flag-tm{background-position:0 87.603306%}.flag-tn{background-position:0 88.016529%}.flag-to{background-position:0 88.429752%}.flag-tp{background-position:0 88.842975%}.flag-tr{background-position:0 89.256198%}.flag-tt{background-position:0 89.669421%}.flag-tv{background-position:0 90.082645%}.flag-tw{background-position:0 90.495868%}.flag-ty{background-position:0 90.909091%}.flag-tz{background-position:0 91.322314%}.flag-ua{background-position:0 91.735537%}.flag-ug{background-position:0 92.14876%}.flag-gb,.flag-uk{background-position:0 92.561983%}.flag-um{background-position:0 92.975207%}.flag-us{background-position:0 93.38843%}.flag-uy{background-position:0 93.801653%}.flag-uz{background-position:0 94.214876%}.flag-va{background-position:0 94.628099%}.flag-vc{background-position:0 95.041322%}.flag-ve{background-position:0 95.454545%}.flag-vg{background-position:0 95.867769%}.flag-vi{background-position:0 96.280992%}.flag-vn{background-position:0 96.694215%}.flag-vu{background-position:0 97.107438%}.flag-wf{background-position:0 97.520661%}.flag-ws{background-position:0 97.933884%}.flag-ye{background-position:0 98.347107%}.flag-za{background-position:0 98.760331%}.flag-zm{background-position:0 99.173554%}.flag-zr{background-position:0 99.586777%}.flag-zw{background-position:0 100%} 2 | -------------------------------------------------------------------------------- /src/app/pages/auth/login.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { RouterModule } from '@angular/router'; 4 | import { ButtonModule } from 'primeng/button'; 5 | import { CheckboxModule } from 'primeng/checkbox'; 6 | import { InputTextModule } from 'primeng/inputtext'; 7 | import { PasswordModule } from 'primeng/password'; 8 | import { RippleModule } from 'primeng/ripple'; 9 | import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; 10 | 11 | @Component({ 12 | selector: 'app-login', 13 | standalone: true, 14 | imports: [ButtonModule, CheckboxModule, InputTextModule, PasswordModule, FormsModule, RouterModule, RippleModule, AppFloatingConfigurator], 15 | template: ` 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 | 29 | 30 | 31 | 32 | 33 | 37 | 38 | 39 |
Welcome to PrimeLand!
40 | Sign in to continue 41 |
42 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 | Forgot password? 56 |
57 | 58 |
59 |
60 |
61 |
62 |
63 | ` 64 | }) 65 | export class Login { 66 | email: string = ''; 67 | 68 | password: string = ''; 69 | 70 | checked: boolean = false; 71 | } 72 | -------------------------------------------------------------------------------- /src/app/layout/component/app.topbar.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MenuItem } from 'primeng/api'; 3 | import { RouterModule } from '@angular/router'; 4 | import { CommonModule } from '@angular/common'; 5 | import { StyleClassModule } from 'primeng/styleclass'; 6 | import { AppConfigurator } from './app.configurator'; 7 | import { LayoutService } from '../service/layout.service'; 8 | 9 | @Component({ 10 | selector: 'app-topbar', 11 | standalone: true, 12 | imports: [RouterModule, CommonModule, StyleClassModule, AppConfigurator], 13 | template: `
14 | 39 | 40 |
41 |
42 | 45 |
46 | 57 | 58 |
59 |
60 | 61 | 64 | 65 | 81 |
82 |
` 83 | }) 84 | export class AppTopbar { 85 | items!: MenuItem[]; 86 | 87 | constructor(public layoutService: LayoutService) {} 88 | 89 | toggleDarkMode() { 90 | this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/app/pages/notfound/notfound.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { ButtonModule } from 'primeng/button'; 4 | import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; 5 | 6 | @Component({ 7 | selector: 'app-notfound', 8 | standalone: true, 9 | imports: [RouterModule, AppFloatingConfigurator, ButtonModule], 10 | template: ` 11 | ` 67 | }) 68 | export class Notfound {} 69 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/footerwidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router, RouterModule } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'footer-widget', 6 | imports: [RouterModule], 7 | template: ` 8 |
9 |
10 | 32 | 33 |
34 |
35 |
36 |

Company

37 | About Us 38 | News 39 | Investor Relations 40 | Careers 41 | Media Kit 42 |
43 | 44 |
45 |

Resources

46 | Get Started 47 | Learn 48 | Case Studies 49 |
50 | 51 |
52 |

Community

53 | Discord 54 | Eventsbadge 55 | FAQ 56 | Blog 57 |
58 | 59 |
60 |

Legal

61 | Brand Policy 62 | Privacy Policy 63 | Terms of Service 64 |
65 |
66 |
67 |
68 |
69 | ` 70 | }) 71 | export class FooterWidget { 72 | constructor(public router: Router) {} 73 | } 74 | -------------------------------------------------------------------------------- /src/app/pages/landing/components/featureswidget.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | @Component({ 5 | selector: 'features-widget', 6 | standalone: true, 7 | imports: [CommonModule], 8 | template: `
9 |
10 |
11 |
Marvelous Features
12 | Placerat in egestas erat... 13 |
14 | 15 |
16 |
17 |
18 |
19 | 20 |
21 |
Easy to Use
22 | Posuere morbi leo urna molestie. 23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
Fresh Design
34 | Semper risus in hendrerit. 35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 |
Well Documented
46 | Non arcu risus quis varius quam quisque. 47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 | 56 |
57 |
Responsive Layout
58 | Nulla malesuada pellentesque elit. 59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 | 68 |
69 |
Clean Code
70 | Condimentum lacinia quis vel eros. 71 |
72 |
73 |
74 | 75 |
76 |
77 |
78 |
79 | 80 |
81 |
Dark Mode
82 | Convallis tellus id interdum velit laoreet. 83 |
84 |
85 |
86 | 87 |
88 |
89 |
90 |
91 | 92 |
93 |
Ready to Use
94 | Mauris sit amet massa vitae. 95 |
96 |
97 |
98 | 99 |
100 |
101 |
102 |
103 | 104 |
105 |
Modern Practices
106 | Elementum nibh tellus molestie nunc non. 107 |
108 |
109 |
110 | 111 |
112 |
113 |
114 |
115 | 116 |
117 |
Privacy
118 | Neque egestas congue quisque. 119 |
120 |
121 |
122 | 123 |
127 |
128 |
Joséphine Miller
129 | Peak Interactive 130 |

131 | “Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.” 132 |

133 | Company logo 134 |
135 |
136 |
137 |
` 138 | }) 139 | export class FeaturesWidget {} 140 | --------------------------------------------------------------------------------