diff --git a/apps/model-ad/app/project.json b/apps/model-ad/app/project.json index cc621d386e..27c8a2e038 100644 --- a/apps/model-ad/app/project.json +++ b/apps/model-ad/app/project.json @@ -7,44 +7,60 @@ "tags": [], "targets": { "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], + "executor": "@angular-devkit/build-angular:browser", + "outputs": [ + "{options.outputPath}" + ], "options": { - "outputPath": "dist/apps/model-ad/app", + "outputPath": "dist/apps/model-ad/app/browser/browser", "index": "apps/model-ad/app/src/index.html", - "browser": "apps/model-ad/app/src/main.ts", - "polyfills": ["zone.js"], + "main": "apps/model-ad/app/src/main.ts", + "polyfills": [ + "zone.js" + ], "tsConfig": "apps/model-ad/app/tsconfig.app.json", "inlineStyleLanguage": "scss", - "assets": ["apps/model-ad/app/src/favicon.ico", "apps/model-ad/app/src/assets"], - "styles": ["apps/model-ad/app/src/styles.scss"], - "scripts": [], - "server": "apps/model-ad/app/src/main.server.ts", - "prerender": true, - "ssr": { - "entry": "apps/model-ad/app/server.ts" - } + "assets": [ + "apps/model-ad/app/src/assets", + "apps/model-ad/app/src/humans.txt", + "apps/model-ad/app/src/robots.txt", + { + "input": "libs/shared/typescript/assets/src/assets", + "glob": "**/*", + "output": "assets" + } + ], + "styles": [ + "apps/model-ad/app/src/styles.scss", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/themes/lara-light-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css" + ], + "scripts": [] }, "configurations": { "production": { "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "1mb", + "maximumError": "2mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", - "maximumError": "4kb" + "maximumError": "10kb" } ], "outputHashing": "all" }, "development": { + "buildOptimizer": false, "optimization": false, + "vendorChunk": true, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "namedChunks": true } }, "defaultConfiguration": "production" @@ -72,7 +88,9 @@ }, "test": { "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], "options": { "jestConfig": "apps/model-ad/app/jest.config.ts" } @@ -84,5 +102,10 @@ "staticFilePath": "dist/apps/model-ad/app/browser" } } - } -} + }, + "implicitDependencies": [ + "model-ad-styles", + "model-ad-themes", + "shared-typescript-assets" + ] +} \ No newline at end of file diff --git a/apps/model-ad/app/src/_app-theme.scss b/apps/model-ad/app/src/_app-theme.scss new file mode 100644 index 0000000000..5b717506d8 --- /dev/null +++ b/apps/model-ad/app/src/_app-theme.scss @@ -0,0 +1,48 @@ +@use 'sass:map'; +@use '@angular/material' as mat; +@use 'libs/model-ad/themes/src/fonts' as fonts; +@use 'libs/model-ad/themes/src/palettes' as palettes; +@use 'libs/model-ad/themes/src/index' as model-ad; + +@include mat.typography-hierarchy(fonts.$lato); +@include mat.core(); + +$primary: mat.define-palette(palettes.$dark-blue-palette, 600); +$accent: mat.define-palette(palettes.$accent-purple-palette, 400); + +$theme: mat.define-light-theme( + ( + color: ( + primary: $primary, + accent: $accent, + ), + typography: fonts.$lato, + density: 0, + is-dark: false, + ) +); + +// Add custom palettes used in figma to the theme +$theme: map.deep-merge( + $theme, + ( + color: ( + figma: palettes.$figma-collection, + ), + ) +); + +// Emit theme-dependent styles for common features used across multiple components. +@include mat.core-theme($theme); + +// Emit styles for MatButton based on `$theme`. +@include mat.button-theme($theme); + +// Include the theme mixins for other components you use here. +@include model-ad.theme($theme); + +:root { + --color-btn-primary: #39bde7; + --color-btn-disabled: #ebebe4; + --color-btn-shadow: rgba(196, 196, 196, 1); +} diff --git a/apps/model-ad/app/src/app/app.component.html b/apps/model-ad/app/src/app/app.component.html index 0f4018b6d7..db2579811d 100644 --- a/apps/model-ad/app/src/app/app.component.html +++ b/apps/model-ad/app/src/app/app.component.html @@ -1 +1,8 @@ + diff --git a/apps/model-ad/app/src/app/app.component.ts b/apps/model-ad/app/src/app/app.component.ts index e5bfabd849..342ee3f7ce 100644 --- a/apps/model-ad/app/src/app/app.component.ts +++ b/apps/model-ad/app/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { NxWelcomeComponent } from './nx-welcome.component'; +import { FooterComponent } from '@sagebionetworks/model-ad/ui'; @Component({ standalone: true, - imports: [NxWelcomeComponent, RouterModule], + imports: [NxWelcomeComponent, RouterModule, FooterComponent], selector: 'app-root', templateUrl: './app.component.html', styleUrl: './app.component.scss', diff --git a/apps/model-ad/app/src/humans.txt b/apps/model-ad/app/src/humans.txt new file mode 100644 index 0000000000..9093f0f578 --- /dev/null +++ b/apps/model-ad/app/src/humans.txt @@ -0,0 +1,6 @@ +/* TEAM */ + +/* THANKS */ + +/* SITE */ +Last update: 2024/05/29 diff --git a/apps/model-ad/app/src/robots.txt b/apps/model-ad/app/src/robots.txt new file mode 100644 index 0000000000..4f9540ba35 --- /dev/null +++ b/apps/model-ad/app/src/robots.txt @@ -0,0 +1 @@ +User-agent: * \ No newline at end of file diff --git a/apps/model-ad/app/src/styles.scss b/apps/model-ad/app/src/styles.scss index 90d4ee0072..8ddd28cc9e 100644 --- a/apps/model-ad/app/src/styles.scss +++ b/apps/model-ad/app/src/styles.scss @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ +@use 'libs/model-ad/styles/src/index'; + +@use 'app-theme'; diff --git a/libs/model-ad/.gitkeep b/libs/model-ad/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/model-ad/styles/README.md b/libs/model-ad/styles/README.md new file mode 100644 index 0000000000..3428bf1e5f --- /dev/null +++ b/libs/model-ad/styles/README.md @@ -0,0 +1 @@ +# model-ad-styles diff --git a/libs/model-ad/styles/project.json b/libs/model-ad/styles/project.json new file mode 100644 index 0000000000..a6d0e044d3 --- /dev/null +++ b/libs/model-ad/styles/project.json @@ -0,0 +1,13 @@ +{ + "name": "model-ad-styles", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/model-ad/styles/src", + "projectType": "library", + "generators": {}, + "targets": {}, + "tags": [ + "type:styles", + "scope:model-ad", + "language:typescript" + ] +} \ No newline at end of file diff --git a/libs/model-ad/styles/src/_index.scss b/libs/model-ad/styles/src/_index.scss new file mode 100644 index 0000000000..144ee1f14c --- /dev/null +++ b/libs/model-ad/styles/src/_index.scss @@ -0,0 +1,4 @@ +@use 'libs/shared/typescript/styles/src/index'; + +@use './lib/constants'; +@use './lib/general'; diff --git a/libs/model-ad/styles/src/lib/_constants.scss b/libs/model-ad/styles/src/lib/_constants.scss new file mode 100644 index 0000000000..ceb3628f29 --- /dev/null +++ b/libs/model-ad/styles/src/lib/_constants.scss @@ -0,0 +1,39 @@ +// See https://github.com/angular/components/blob/master/src/material/core/style/_variables.scss + +// Global constants +$pi: 3.14159264; +$padding-test: 20px; + +// Figma variables +$dl-size-size-large: 144px; +$dl-size-size-small: 48px; +$dl-size-size-medium: 96px; +$dl-size-size-xlarge: 192px; +$dl-size-size-xsmall: 16px; +$dl-space-space-unit: 16px; +$dl-size-size-xxlarge: 288px; +$dl-size-size-maxwidth: 1400px; +$dl-radius-radius-round: 50%; +$dl-space-space-halfunit: 8px; +$dl-space-space-sixunits: 96px; +$dl-space-space-twounits: 32px; +$dl-radius-radius-radius2: 2px; +$dl-radius-radius-radius4: 4px; +$dl-radius-radius-radius8: 8px; +$dl-space-space-fiveunits: 80px; +$dl-space-space-fourunits: 64px; +$dl-radius-radius-radius16: 16px; +$dl-space-space-threeunits: 48px; +$dl-space-space-oneandhalfunits: 24px; + +// Breakpoints from https://getbootstrap.com/docs/5.0/layout/breakpoints/ +$sm-breakpoint: 576px; +$md-breakpoint: 768px; +$lg-breakpoint: 992px; +$xl-breakpoint: 1200px; +$xxl-breakpoint: 1400px; + +// Dimensions of component +$navbar-height: 68px; +$navbar-height-tall: 240px; +$footer-height: 259px; diff --git a/libs/model-ad/styles/src/lib/_general.scss b/libs/model-ad/styles/src/lib/_general.scss new file mode 100644 index 0000000000..e774e40dbb --- /dev/null +++ b/libs/model-ad/styles/src/lib/_general.scss @@ -0,0 +1,432 @@ +@use 'libs/model-ad/styles/src/lib/constants'; + +html { + margin-top: constants.$navbar-height; +} +body { + max-height: 100vh; + margin: 0; +} +em { + color: #00b1e5; + font-style: normal; + font-weight: 700; +} + +// GRID +.base { + width: 100%; + min-height: calc(100vh - constants.$navbar-height - constants.$footer-height); +} +.row { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} +.col { + display: flex; + flex-direction: column; + flex-basis: 100%; +} +.fill-empty { + max-width: 920px; +} +.content { + @extend .row; + max-width: 2000px; + margin: auto; + padding: 48px 32px; + gap: 36px; +} + +// TEXT +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.text-grey, +span.text-grey a { + color: rgba(black, 0.38); +} +.text-right { + font-weight: 600; +} + +// LOGO & DESIGN +.logo-icon { + width: 140px; + height: 140px; +} +.top-design { + background: url('/model-ad-assets/images/banner-sm.svg'); + background-size: cover; + background-position: bottom; + padding-top: 108px; + text-align: center; + + .section-inner { + margin-bottom: 40px; + + h2 { + margin: 8px 0; + } + } +} + +// BUTTONS +.btn { + padding: 6px 32px !important; + text-transform: uppercase; + text-decoration: none; + transition: background 0.15s ease !important; +} +.btn-block { + width: 100%; + padding: 16px 32px !important; + text-transform: uppercase; + text-decoration: none; +} +.btn-group { + display: flex; + flex-flow: column wrap; + align-content: center; + + a { + text-decoration: none; + } +} +.btn-group > *:first-child { + align-self: stretch; +} +.btn-group button, +.btn-group a { + margin: 5px; + padding: 21px 32px !important; +} + +// SEARCH PAGES +#search-top { + padding: 0 32px 80px; +} +.search-sort-container { + gap: 24px; + justify-content: space-between; +} +.search-field { + position: relative; + flex-grow: 1; + margin: auto; +} +.sort-field { + display: flex; + gap: 16px; + align-items: center; +} +.sort-title { + white-space: nowrap; +} +.facets { + padding: 24px 16px; + height: 100%; + min-width: 280px; + display: flex; + align-items: flex-start; + flex-direction: column; + border-style: solid; + border-width: 2px; + border-radius: 8px; + + & > * { + width: 100%; + } +} + +// PROFILE PAGES +#profile-top, +#profile-bottom { + min-height: 320px; + justify-content: center; +} +#profile-top { + padding-top: 60px; +} +#profile-bottom { + padding-top: 32px; +} +#profile-top > div { + align-self: center; + flex: 1 0 auto; + padding: 22px; +} +.profile-pic, +.organization-card-banner { + // width: 40px; + // object-fit: cover; + margin: 8px; + align-self: center; + flex: 0 0 auto; + box-sizing: border-box; + border-radius: constants.$dl-radius-radius-round; + box-shadow: 1px 5px 18px 0px #d4d4d4; + + div.avatar-content { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } +} +#profile-details { + max-width: 100%; + text-align: center; +} +.profile-activity-card { + text-align: right; +} +.profile-nav-group { + margin: 18px; + padding: 48px 18px; + display: flex; + align-items: flex-start; + flex-direction: column; + border-style: solid; + border-width: 2px; + border-radius: 8px; +} +.profile-nav-item { + width: 100%; + height: 54px; + margin: 0 0 18px 0; + padding: 13px 18px; + box-sizing: border-box; + transition: 0.3s; + align-items: flex-start; + flex-shrink: 1; + text-align: left; + text-decoration: none; +} +.profile-type { + width: 100%; + max-width: 148px; + margin: 8px 0; + padding: 5px; + border-radius: 32px; + text-align: center; + align-self: center; +} +.stats-group { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + justify-content: center; +} +.stat-item { + flex-direction: column; + flex: 1; + max-width: 120px; + padding: 18px 5px 8px; + border-style: solid; + border-width: 1px; + text-align: center; + border-color: var(--color-btn-shadow); + background-color: white; + border-right-width: 0; + + h3 { + margin-bottom: 2px !important; + } +} +.action-btn, +.disabled-btn { + height: 100%; + min-width: 260px; + max-width: 280px; + padding: 13px 5px; + display: inline-flex; + flex-direction: row; + justify-content: center; + align-items: center; +} +.action-btn { + background-color: var(--color-btn-primary); + color: white; + border-color: transparent; + cursor: pointer; +} +.disabled-btn { + background-color: var(--color-btn-disabled); + border-right-width: 1px; +} +.stats-card-icon { + margin-right: 4px; +} +table { + border-spacing: 11px; +} +#bio div a mat-icon { + font-size: 15px; + vertical-align: middle; +} +.created-updated-dates, +.created-updated-dates a, +.read-more { + color: #1f3b8f; +} +.created-updated-dates { + font-style: italic !important; +} +.read-more { + text-underline-offset: 3px; +} + +// CARDS +@mixin line-clamp($max-lines) { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: $max-lines; +} +.card-group { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 24px; + padding: 16px 0; +} +.card-banner { + width: 100%; + height: 130px; + display: flex; + align-items: flex-start; +} +.card-icon { + margin: 0 3px; + padding-right: 24px; +} +.card-title { + margin: 15px 0 !important; + font-weight: 700 !important; + line-height: 24px !important; + min-height: 48px; + @include line-clamp(2); +} +.mat-caption { + @include line-clamp(2); +} +.card-body { + width: 100%; + margin: 8px; + display: flex; + flex: 1; + align-items: center; + flex-direction: column; + justify-content: flex-start; + + & > p { + text-align: center; + } +} +.card-footer { + display: flex; + flex-direction: row; + height: 50px; + width: 100%; + border-top: 1px solid #c4c4c4; + justify-content: center; + align-items: center; + gap: 6px; +} +.star-btn { + position: absolute; + top: 18px; + right: 18px; + // height: 32px; + padding: 0 8px; + display: flex; + justify-content: center; + align-items: center; + border-radius: constants.$dl-radius-radius-radius16; + cursor: pointer; +} + +// MEDIA QUERIES +@media screen and (max-width: constants.$sm-breakpoint) { + .profile-activity-card { + text-align: center; + } +} +@media screen and (max-width: constants.$md-breakpoint) { + .content { + flex-direction: column; + } +} +@media screen and (min-width: constants.$md-breakpoint) { + .col, + .col-1 { + flex: 1; + } + .col-2 { + flex: 2; + } + .col-3 { + flex: 3; + } + .col-4 { + flex: 4; + } + .col-5 { + flex: 5; + } + .col-6 { + flex: 6; + } + .col-7 { + flex: 7; + } + .col-8 { + flex: 8; + } + .col-9 { + flex: 9; + } + .col-10 { + flex: 10; + } + .col-11 { + flex: 11; + } + .col-12 { + flex: 12; + } + #search-top { + padding: 0 132px 80px; + } + #profile-details { + // max-width: 760px; + max-width: 1060px; + text-align: left; + } + .profile-type { + align-self: auto; + } + .profile-sidenav { + max-width: 320px; + } +} +@media only screen and (max-width: constants.$lg-breakpoint) { + html { + margin-top: constants.$navbar-height-tall; + } + + .card-group { + grid-template-columns: 60%; + justify-content: center; + } +} diff --git a/libs/model-ad/themes/README.md b/libs/model-ad/themes/README.md new file mode 100644 index 0000000000..22855b7383 --- /dev/null +++ b/libs/model-ad/themes/README.md @@ -0,0 +1 @@ +# model-ad-themes \ No newline at end of file diff --git a/libs/model-ad/themes/project.json b/libs/model-ad/themes/project.json new file mode 100644 index 0000000000..2b9272fd60 --- /dev/null +++ b/libs/model-ad/themes/project.json @@ -0,0 +1,14 @@ +{ + "name": "model-ad-themes", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/model-ad/themes/src", + "projectType": "library", + "generators": {}, + "targets": {}, + "tags": [ + "type:themes", + "scope:model-ad", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/model-ad/themes/src/_fonts.scss b/libs/model-ad/themes/src/_fonts.scss new file mode 100644 index 0000000000..f4f0388601 --- /dev/null +++ b/libs/model-ad/themes/src/_fonts.scss @@ -0,0 +1,16 @@ +@use '@angular/material' as mat; + +@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap'); + + +$lato: mat.define-typography-config( + $font-family: "'Lato', sans-serif", + $headline-5: mat.define-typography-level(46px, 56px, 700), + $headline-6: mat.define-typography-level(40px, 50px, 700), + $subtitle-1: mat.define-typography-level(28px, 38px, 700, $letter-spacing: -0.1px), + $subtitle-2: mat.define-typography-level(20px, 32px, 400, $letter-spacing: -0.1px), + $body-1: mat.define-typography-level(21px, 36px, 400, $letter-spacing: -0.1px), + $body-2: mat.define-typography-level(16px, 26px, 400), + $caption: mat.define-typography-level(14px, 21px, 400), + $button: mat.define-typography-level(16px, 18px, 700), +); diff --git a/libs/model-ad/themes/src/_index.scss b/libs/model-ad/themes/src/_index.scss new file mode 100644 index 0000000000..de22118d94 --- /dev/null +++ b/libs/model-ad/themes/src/_index.scss @@ -0,0 +1,5 @@ +@use 'libs/model-ad/ui/src/lib-theme' as model-ad-ui; + +@mixin theme($theme) { + @include model-ad-ui.theme($theme); +} diff --git a/libs/model-ad/themes/src/_palettes.scss b/libs/model-ad/themes/src/_palettes.scss new file mode 100644 index 0000000000..815a96839d --- /dev/null +++ b/libs/model-ad/themes/src/_palettes.scss @@ -0,0 +1,135 @@ +@use 'sass:map'; + +// Color variables +$dark-primary-text: rgba(black, 0.87); +$dark-secondary-text: rgba(black, 0.54); +$dark-disabled-text: rgba(black, 0.38); +$dark-dividers: rgba(black, 0.12); +$dark-focused: rgba(black, 0.12); +$light-primary-text: white; +$light-secondary-text: rgba(white, 0.7); +$light-disabled-text: rgba(white, 0.5); +$light-dividers: rgba(white, 0.12); +$light-focused: rgba(white, 0.12); + +$dark-blue-palette: ( + 50: #e8ebf5, + 100: #c4cce7, + 200: #9dabd7, + 300: #768ac7, + 400: #586fbc, + 500: #3756b1, + 600: #314fa7, + 700: #27459b, + 800: #1f3b8f, + 900: #102979, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $light-primary-text, + 500: $light-primary-text, + 600: $light-primary-text, + 700: $light-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +$light-blue-palette: ( + 50: #dff4fb, + 100: #ade3f4, + 200: #76d1ed, + 300: #39bde7, + 400: #00b1e5, + 500: #00a4e3, + 600: #0096d5, + 700: #0084c3, + 800: #0073b0, + 900: #005390, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $dark-primary-text, + 500: $dark-primary-text, + 600: $dark-primary-text, + 700: $dark-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +$light-green-palette: ( + 50: #e8fefb, + 100: #c6fdf3, + 200: #a5fdec, + 300: #88f9e3, + 400: #76f2d9, + 500: #71ecd0, + 600: #6adbbf, + 700: #61c8ad, + 800: #5bb79d, + 900: #50977e, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $dark-primary-text, + 500: $dark-primary-text, + 600: $dark-primary-text, + 700: $dark-primary-text, + 800: $dark-primary-text, + 900: $dark-primary-text, + ), +); + +$light-purple-palette: ( + 50: #ede8ff, + 100: #d0c6fd, + 200: #afa0fe, + 300: #8a78ff, + 400: #6859ff, + 500: #594AFC, + 600: #2e3aee, + 700: #0032e5, + 800: #002ddc, + 900: #0023ce, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $light-primary-text, + 500: $light-primary-text, + 600: $light-primary-text, + 700: $light-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +// Alias for "accent" usage +$accent-green-palette: $light-green-palette; +$accent-purple-palette: $light-purple-palette; + +// Figma palettes variables +$figma-collection: ( + dl-color-default-navbar: map.get($dark-blue-palette, 600), + dl-color-default-accent1: map.get($accent-green-palette, 100), + dl-color-default-accent2: map.get($accent-purple-palette, 50), + dl-color-default-primary1: map.get($dark-blue-palette, 400), + dl-color-default-primary2: $dark-focused, + dl-color-default-navbardark: map.get($dark-blue-palette, 800), + dl-color-default-secondary1: map.get($light-blue-palette, 300), + dl-color-default-secondary2: map.get($accent-purple-palette, 300), + dl-color-default-darkaccent1: map.get($accent-green-palette, 700), + dl-color-default-darkaccent2: map.get($accent-purple-palette, 200), + dl-color-gray-black: #000000, + dl-color-gray-white: #ffffff, + dl-color-default-hover1: rgba(249, 249, 249, 1), + dl-color-default-hover2: rgba(229, 229, 229, 1), +); diff --git a/libs/model-ad/ui/.eslintrc.json b/libs/model-ad/ui/.eslintrc.json new file mode 100644 index 0000000000..921d4e5307 --- /dev/null +++ b/libs/model-ad/ui/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "model-ad", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "model-ad", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/model-ad/ui/README.md b/libs/model-ad/ui/README.md new file mode 100644 index 0000000000..53bd98408c --- /dev/null +++ b/libs/model-ad/ui/README.md @@ -0,0 +1,7 @@ +# model-ad-ui + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test model-ad-ui` to execute the unit tests. diff --git a/libs/model-ad/ui/jest.config.ts b/libs/model-ad/ui/jest.config.ts new file mode 100644 index 0000000000..0c34499915 --- /dev/null +++ b/libs/model-ad/ui/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'model-ad-ui', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/model-ad/ui', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/model-ad/ui/project.json b/libs/model-ad/ui/project.json new file mode 100644 index 0000000000..b4b9e13f1d --- /dev/null +++ b/libs/model-ad/ui/project.json @@ -0,0 +1,27 @@ +{ + "name": "model-ad-ui", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/model-ad/ui/src", + "prefix": "model-ad", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/model-ad/ui" + ], + "options": { + "jestConfig": "libs/model-ad/ui/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + }, + "tags": [ + "type:feature", + "scope:model-ad", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/model-ad/ui/src/_lib-theme.scss b/libs/model-ad/ui/src/_lib-theme.scss new file mode 100644 index 0000000000..f5d492663f --- /dev/null +++ b/libs/model-ad/ui/src/_lib-theme.scss @@ -0,0 +1,5 @@ +@use './lib/footer/footer-theme' as footer; + +@mixin theme($theme) { + @include footer.theme($theme); +} diff --git a/libs/model-ad/ui/src/index.ts b/libs/model-ad/ui/src/index.ts new file mode 100644 index 0000000000..e70ebd86fe --- /dev/null +++ b/libs/model-ad/ui/src/index.ts @@ -0,0 +1 @@ +export * from './lib/footer/footer.component'; diff --git a/libs/model-ad/ui/src/lib/footer/_footer-theme.scss b/libs/model-ad/ui/src/lib/footer/_footer-theme.scss new file mode 100644 index 0000000000..fee88f2524 --- /dev/null +++ b/libs/model-ad/ui/src/lib/footer/_footer-theme.scss @@ -0,0 +1,60 @@ +@use 'sass:map'; +@use '@angular/material' as mat; +@use 'libs/model-ad/styles/src/lib/constants'; + +@mixin color($theme) { + $config: mat.get-color-config($theme); + $primary: map.get($config, 'primary'); + $accent: map.get($config, 'accent'); + $warn: map.get($config, 'warn'); + + footer { + background-color: mat.get-color-from-palette($primary, 600); + color: #fff; + } + .footer-link-group a, + .footer-links a { + color: #fff; + } + .footer-bottom { + background-color: mat.get-color-from-palette($primary, 800); + } +} + +@mixin typography($theme) { + $config: mat.get-typography-config($theme); + + footer { + font-weight: 500; + line-height: normal; + } + .app-info { + font-size: 14px; + line-height: 21px; + } + .footer-link-group { + font-size: 16px; + } + .footer-subtext, + .footer-links, + .footer-links a { + font-size: 16px; + } + @media only screen and (max-width: constants.$md-breakpoint) { + .footer-link-group { + font-size: 14px !important; + } + } +} + +@mixin theme($theme) { + $color-config: mat.get-color-config($theme); + @if $color-config != null { + @include color($theme); + } + + $typography-config: mat.get-typography-config($theme); + @if $typography-config != null { + @include typography($theme); + } +} diff --git a/libs/model-ad/ui/src/lib/footer/footer.component.html b/libs/model-ad/ui/src/lib/footer/footer.component.html new file mode 100644 index 0000000000..8bb1f00464 --- /dev/null +++ b/libs/model-ad/ui/src/lib/footer/footer.component.html @@ -0,0 +1,41 @@ + diff --git a/libs/model-ad/ui/src/lib/footer/footer.component.scss b/libs/model-ad/ui/src/lib/footer/footer.component.scss new file mode 100644 index 0000000000..f9fb0b6e21 --- /dev/null +++ b/libs/model-ad/ui/src/lib/footer/footer.component.scss @@ -0,0 +1,71 @@ +@use 'libs/model-ad/styles/src/lib/constants'; + +footer { + width: 100%; + height: constants.$footer-height; + position: relative; + padding: 42px 52px; + display: flex; + flex-direction: column; + align-items: flex-start; + box-sizing: border-box; +} +.footer-link-group { + width: 420px; + height: 30px; + padding: 0; + display: flex; + justify-content: space-between; + list-style: none; +} +.footer-link-group li { + flex: 0 0 auto; +} +.app-info { + text-align: right; + align-self: center; +} +.app-info ul { + list-style-type: none; + padding: 0; + margin: 0; +} +.footer-bottom { + bottom: 0; + left: 0px; + width: 100%; + height: 70px; + display: flex; + position: absolute; + align-items: center; + justify-content: center; +} +.footer-links, +.footer-links a { + text-decoration: none; +} +.footer-links a:hover { + text-decoration: underline; +} +.logo { + height: 56px; +} + +@media only screen and (max-width: constants.$md-breakpoint) { + footer { + height: 410px; + } + .about-oc, + .app-info, + .footer-link-group { + width: 100%; + text-align: center; + justify-content: space-around; + } + .footer-link-group li { + flex: 1; + } + .app-info { + margin-top: 45px; + } +} diff --git a/libs/model-ad/ui/src/lib/footer/footer.component.spec.ts b/libs/model-ad/ui/src/lib/footer/footer.component.spec.ts new file mode 100644 index 0000000000..48a1b8798f --- /dev/null +++ b/libs/model-ad/ui/src/lib/footer/footer.component.spec.ts @@ -0,0 +1,25 @@ +import { HttpClientModule } from '@angular/common/http'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { FooterComponent } from './footer.component'; + +describe('FooterComponent', () => { + let component: FooterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HttpClientModule, RouterTestingModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/libs/model-ad/ui/src/lib/footer/footer.component.ts b/libs/model-ad/ui/src/lib/footer/footer.component.ts new file mode 100644 index 0000000000..5ea766931c --- /dev/null +++ b/libs/model-ad/ui/src/lib/footer/footer.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'model-ad-footer', + standalone: true, + imports: [CommonModule, RouterModule], + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'], +}) +export class FooterComponent { + @Input({ required: true }) appVersion = ''; + @Input({ required: true }) dataUpdatedOn = ''; + @Input({ required: true }) privacyPolicyUrl = ''; + @Input({ required: true }) termsOfUseUrl = ''; + @Input({ required: true }) apiDocsUrl = ''; +} diff --git a/libs/model-ad/ui/src/test-setup.ts b/libs/model-ad/ui/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/model-ad/ui/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/model-ad/ui/tsconfig.json b/libs/model-ad/ui/tsconfig.json new file mode 100644 index 0000000000..f96ad85d2e --- /dev/null +++ b/libs/model-ad/ui/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "target": "es2020" + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/libs/model-ad/ui/tsconfig.lib.json b/libs/model-ad/ui/tsconfig.lib.json new file mode 100644 index 0000000000..b228a1a081 --- /dev/null +++ b/libs/model-ad/ui/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/model-ad/ui/tsconfig.spec.json b/libs/model-ad/ui/tsconfig.spec.json new file mode 100644 index 0000000000..d3889a9881 --- /dev/null +++ b/libs/model-ad/ui/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index a6192bd034..4d136dd3e6 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,46 +10,89 @@ "importHelpers": true, "target": "es2015", "module": "esnext", - "lib": ["es2017", "dom"], + "lib": [ + "es2017", + "dom" + ], "skipLibCheck": true, "skipDefaultLibCheck": true, "forceConsistentCasingInFileNames": true, "strict": true, "baseUrl": ".", "paths": { - "@sagebionetworks/openchallenges/about": ["libs/openchallenges/about/src/index.ts"], + "@sagebionetworks/openchallenges/about": [ + "libs/openchallenges/about/src/index.ts" + ], "@sagebionetworks/openchallenges/api-client-angular": [ "libs/openchallenges/api-client-angular/src/index.ts" ], - "@sagebionetworks/openchallenges/assets": ["libs/openchallenges/assets/src/index.ts"], - "@sagebionetworks/openchallenges/auth": ["libs/openchallenges/auth/src/index.ts"], - "@sagebionetworks/openchallenges/challenge": ["libs/openchallenges/challenge/src/index.ts"], + "@sagebionetworks/openchallenges/assets": [ + "libs/openchallenges/assets/src/index.ts" + ], + "@sagebionetworks/openchallenges/auth": [ + "libs/openchallenges/auth/src/index.ts" + ], + "@sagebionetworks/openchallenges/challenge": [ + "libs/openchallenges/challenge/src/index.ts" + ], "@sagebionetworks/openchallenges/challenge-search": [ "libs/openchallenges/challenge-search/src/index.ts" ], - "@sagebionetworks/openchallenges/config": ["libs/openchallenges/config/src/index.ts"], - "@sagebionetworks/openchallenges/home": ["libs/openchallenges/home/src/index.ts"], - "@sagebionetworks/openchallenges/login": ["libs/openchallenges/login/src/index.ts"], - "@sagebionetworks/openchallenges/not-found": ["libs/openchallenges/not-found/src/index.ts"], + "@sagebionetworks/openchallenges/config": [ + "libs/openchallenges/config/src/index.ts" + ], + "@sagebionetworks/openchallenges/home": [ + "libs/openchallenges/home/src/index.ts" + ], + "@sagebionetworks/openchallenges/login": [ + "libs/openchallenges/login/src/index.ts" + ], + "@sagebionetworks/openchallenges/not-found": [ + "libs/openchallenges/not-found/src/index.ts" + ], "@sagebionetworks/openchallenges/org-profile": [ "libs/openchallenges/org-profile/src/index.ts" ], - "@sagebionetworks/openchallenges/org-search": ["libs/openchallenges/org-search/src/index.ts"], - "@sagebionetworks/openchallenges/pages": ["libs/openchallenges/pages/src/index.ts"], - "@sagebionetworks/openchallenges/signup": ["libs/openchallenges/signup/src/index.ts"], - "@sagebionetworks/openchallenges/styles": ["libs/openchallenges/styles/src/index.ts"], - "@sagebionetworks/openchallenges/team": ["libs/openchallenges/team/src/index.ts"], - "@sagebionetworks/openchallenges/themes": ["libs/openchallenges/themes/src/index.ts"], - "@sagebionetworks/openchallenges/ui": ["libs/openchallenges/ui/src/index.ts"], + "@sagebionetworks/openchallenges/org-search": [ + "libs/openchallenges/org-search/src/index.ts" + ], + "@sagebionetworks/openchallenges/pages": [ + "libs/openchallenges/pages/src/index.ts" + ], + "@sagebionetworks/openchallenges/signup": [ + "libs/openchallenges/signup/src/index.ts" + ], + "@sagebionetworks/openchallenges/styles": [ + "libs/openchallenges/styles/src/index.ts" + ], + "@sagebionetworks/openchallenges/team": [ + "libs/openchallenges/team/src/index.ts" + ], + "@sagebionetworks/openchallenges/themes": [ + "libs/openchallenges/themes/src/index.ts" + ], + "@sagebionetworks/openchallenges/ui": [ + "libs/openchallenges/ui/src/index.ts" + ], "@sagebionetworks/openchallenges/user-profile": [ "libs/openchallenges/user-profile/src/index.ts" ], - "@sagebionetworks/openchallenges/util": ["libs/openchallenges/util/src/index.ts"], - "@sagebionetworks/shared/util": ["libs/shared/typescript/util/src/index.ts"], + "@sagebionetworks/openchallenges/util": [ + "libs/openchallenges/util/src/index.ts" + ], + "@sagebionetworks/shared/util": [ + "libs/shared/typescript/util/src/index.ts" + ], "@sagebionetworks/shared/web-components": [ "libs/shared/typescript/web-components/src/index.ts" + ], + "@sagebionetworks/model-ad/ui": [ + "libs/model-ad/ui/src/index.ts" ] } }, - "exclude": ["node_modules", "tmp"] -} + "exclude": [ + "node_modules", + "tmp" + ] +} \ No newline at end of file