feat: create challenge 48 without solution

This commit is contained in:
Timothy Alcaide
2024-04-10 02:06:31 +02:00
committed by thomas
parent 94171adfd1
commit c5dd953193
31 changed files with 462 additions and 30003 deletions

View File

@@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f
## Challenges ## Challenges
Check [all 47 challenges](https://angular-challenges.vercel.app/) Check [all 48 challenges](https://angular-challenges.vercel.app/)
## Contributors ✨ ## Contributors ✨

View File

@@ -0,0 +1,36 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,11 @@
# Save your form's bacon: with the navigation alert
> author: [Timothy Alcaide](https://github.com/alcaidio)
### Run Application
```bash
npx nx serve angular-dialog-alert-form
```
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/48-dialog-alert-form/).

View File

@@ -0,0 +1,72 @@
{
"name": "angular-dialog-alert-form",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/angular/dialog-alert-form/src",
"tags": [],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/angular/dialog-alert-form",
"index": "apps/angular/dialog-alert-form/src/index.html",
"browser": "apps/angular/dialog-alert-form/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/angular/dialog-alert-form/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"apps/angular/dialog-alert-form/src/favicon.ico",
"apps/angular/dialog-alert-form/src/assets"
],
"styles": ["apps/angular/dialog-alert-form/src/styles.scss"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "angular-dialog-alert-form:build:production"
},
"development": {
"buildTarget": "angular-dialog-alert-form:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "angular-dialog-alert-form:build"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { NavComponent } from './ui/nav.component';
@Component({
standalone: true,
imports: [RouterOutlet, NavComponent],
selector: 'app-root',
template: `
<div class="h-screen bg-gray-50">
<app-nav
class="mx-auto flex w-full items-center justify-center pb-2 pt-8" />
<main class="px-4 py-16 sm:px-6 lg:px-8">
<router-outlet></router-outlet>
</main>
</div>
`,
})
export class AppComponent {}

View File

@@ -0,0 +1,7 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(appRoutes)],
};

View File

@@ -0,0 +1,29 @@
import { Route } from '@angular/router';
import { JoinComponent } from './pages/join.component';
import { PageComponent } from './pages/page.component';
export const appRoutes: Route[] = [
{
path: '',
pathMatch: 'full',
redirectTo: 'form',
},
{
path: 'form',
loadComponent: () => JoinComponent,
},
{
path: 'page-1',
data: {
title: 'Page 1',
},
loadComponent: () => PageComponent,
},
{
path: 'page-2',
data: {
title: 'Page 2',
},
loadComponent: () => PageComponent,
},
];

View File

@@ -0,0 +1,16 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormComponent } from '../ui/form.component';
@Component({
standalone: true,
imports: [FormComponent],
template: `
<section class="mx-auto max-w-screen-sm">
<div class="rounded-lg bg-white p-8 shadow-lg lg:p-12">
<app-form />
</div>
</section>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class JoinComponent {}

View File

@@ -0,0 +1,17 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs';
@Component({
standalone: true,
template: `
<section>
<h1>{{ title() }}</h1>
</section>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PageComponent {
title = toSignal(inject(ActivatedRoute).data.pipe(map((d) => d['title'])));
}

View File

@@ -0,0 +1,62 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-form',
standalone: true,
template: `
<form class="space-y-4">
<div>
<label class="sr-only" for="name">Name</label>
<input
class="w-full rounded-lg border-gray-200 p-3 text-sm"
placeholder="Name"
type="text"
name="name"
id="name" />
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label class="sr-only" for="email">Email</label>
<input
class="w-full rounded-lg border-gray-200 p-3 text-sm"
placeholder="Email address"
type="email"
name="email"
id="email" />
</div>
<div>
<label class="sr-only" for="phone">Phone</label>
<input
class="w-full rounded-lg border-gray-200 p-3 text-sm"
placeholder="Phone Number"
type="tel"
name="phone"
id="phone" />
</div>
</div>
<div>
<label class="sr-only" for="message">Message</label>
<textarea
class="w-full rounded-lg border-gray-200 p-3 text-sm"
placeholder="Message"
rows="8"
name="message"
id="message"></textarea>
</div>
<div class="mt-4">
<button
type="submit"
class="inline-block w-full rounded-lg border bg-gray-50 px-5 py-3 font-medium text-gray-900 sm:w-auto">
Submit
</button>
</div>
</form>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FormComponent {}

View File

@@ -0,0 +1,33 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-nav',
standalone: true,
imports: [RouterLink, RouterLinkActive],
template: `
<nav
class="inline-flex -space-x-px overflow-hidden rounded-md border bg-white shadow-sm">
<a
routerLink="/form"
routerLinkActive="bg-gray-900 text-gray-50 hover:bg-none"
class="inline-block px-4 py-2 text-sm font-medium text-gray-700 focus:relative">
Form
</a>
<a
routerLink="/page-1"
routerLinkActive="bg-gray-900 text-gray-50 hover:bg-none"
class="inline-block px-4 py-2 text-sm font-medium text-gray-700 focus:relative">
Page 1
</a>
<a
routerLink="/page-2"
routerLinkActive="bg-gray-900 text-gray-50 hover:bg-none"
class="inline-block px-4 py-2 text-sm font-medium text-gray-700 focus:relative">
Page 2
</a>
</nav>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavComponent {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>angular-dialog-alert-form</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);

View File

@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can add global styles to this file, and also import other style files */

View File

@@ -0,0 +1,16 @@
const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
const { join } = require('path');
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
...createGlobPatternsForDependencies(__dirname),
],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
],
};

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": []
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}

View File

@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"compilerOptions": {},
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}

View File

@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "es2022",
"useDefineForClassFields": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.editor.json"
},
{
"path": "./tsconfig.app.json"
}
],
"extends": "../../../tsconfig.base.json",
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -1,6 +1,6 @@
{ {
"total": 47, "total": 48,
"🟢": 18, "🟢": 18,
"🟠": 120, "🟠": 121,
"🔴": 209 "🔴": 209
} }

View File

@@ -0,0 +1,48 @@
---
title: 🟠 Save your form&#39;s bacon: with the navigation alert
description: Challenge 48 is about Bob 🧙‍♂️ the product owner, he wants to develop a new feature in response to customer complaints about losing form input information.
author: Timothy Alcaide
contributors:
- tomalaforge
challengeNumber: 48
command: angular-dialog-alert-form
sidebar:
order: 121
badge: New
---
:::note
WIP: The following documentation need to be written.
:::
## Context
As a member of the development team, you need to address a specific request from the product owner, Bob 🧙‍♂️ He wants to develop a new feature in response to customer complaints about losing form input information.
## User Story
Here's the feature expressed as a user story (functional expectation)
"As a user, I would like to have an alert dialog box that appears when I attempt to navigate away from the /form page after I have started entering information into the form."
## Acceptance Criteria
1. On the /form page:
- If the input text has been pre-filled, then an alert dialog box opens.
- Otherwise, the user navigates normally.
2. The alert dialog box must be clear and concise, stating: "Caution! You have unsaved information. Do you want to continue and lose them?" with the following button labels:
- Continue without saving
- Stay on page
3. The appearance and behavior of the alert dialog box must comply with W3C conventions, see [alert dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/)
4. Maximize the use of the new concepts and syntax in the latest version of Angular.
5. (Bonus) : Try to adopt a declarative approach (for dialog opening, for example).
<details>
<summary>Tips 🤫 (if you really need it and after careful consideration)</summary>
Use the Material CDK Dialog - https://material.angular.io/cdk/dialog/overview
Use the CanDeactivate guard - https://angular.io/api/router/CanDeactivate (use new functionnal approach).
</details>

View File

@@ -9,7 +9,6 @@ challengeNumber: 47
command: typescript-enums-vs-union-types command: typescript-enums-vs-union-types
sidebar: sidebar:
order: 18 order: 18
badge: New
--- ---
## Information ## Information

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow icon: right-arrow
variant: primary variant: primary
- text: Ir al Desafío más reciente - text: Ir al Desafío más reciente
link: /es/challenges/typescript/47-enums-vs-union-types/ link: /es/challenges/angular/48-dialog-alert-form/
icon: rocket icon: rocket
- text: Dar una estrella - text: Dar una estrella
link: https://github.com/tomalaforge/angular-challenges link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro'; import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="47 Desafíos"> <Card title="48 Desafíos">
Este repositorio contiene 47 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>. Este repositorio contiene 48 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades. Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades.
</Card> </Card>

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow icon: right-arrow
variant: primary variant: primary
- text: Aller au dernier Challenge - text: Aller au dernier Challenge
link: /fr/challenges/typescript/47-enums-vs-union-types/ link: /fr/challenges/angular/48-dialog-alert-form/
icon: rocket icon: rocket
- text: Donne une étoile - text: Donne une étoile
link: https://github.com/tomalaforge/angular-challenges link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro'; import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="47 Défis"> <Card title="48 Défis">
Ce répertoire rassemble 47 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences. Ce répertoire rassemble 48 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
</Card> </Card>
<Card title="Subscribe to get notify of latest challenges"> <Card title="Subscribe to get notify of latest challenges">

View File

@@ -26,10 +26,9 @@ import MyIcon from '../../components/MyIcon.astro';
import SubscriptionForm from '../../components/SubscriptionForm.astro'; import SubscriptionForm from '../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="47 Challenges"> <Card title="48 Challenges">
This repository gathers 47 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>. This repository gathers 48 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
These challenges resolve around real-life issues or specific features to elevate your skills. These challenges resolve around real-life issues or specific features to elevate your skills.
</Card> </Card>

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow icon: right-arrow
variant: primary variant: primary
- text: Ir para o desafio mais recente - text: Ir para o desafio mais recente
link: /pt/challenges/typescript/47-enums-vs-union-types/ link: /pt/challenges/angular/48-dialog-alert-form/
icon: rocket icon: rocket
- text: Dar uma estrela - text: Dar uma estrela
link: https://github.com/tomalaforge/angular-challenges link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro'; import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="47 Desafios"> <Card title="48 Desafios">
Este repositório possui 47 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, Este repositório possui 48 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
<b>Ngrx</b> e <b>Typescript</b>. <b>Ngrx</b> e <b>Typescript</b>.
Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de
melhorar suas habilidades. melhorar suas habilidades.

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow icon: right-arrow
variant: primary variant: primary
- text: Перейти к последней задаче - text: Перейти к последней задаче
link: /ru/challenges/typescript/47-enums-vs-union-types/ link: /ru/challenges/angular/48-dialog-alert-form/
icon: rocket icon: rocket
- text: Добавить звезду - text: Добавить звезду
link: https://github.com/tomalaforge/angular-challenges link: https://github.com/tomalaforge/angular-challenges

11
nx.json
View File

@@ -30,6 +30,11 @@
"{workspaceRoot}/tools/eslint-rules/**/*" "{workspaceRoot}/tools/eslint-rules/**/*"
], ],
"cache": true "cache": true
},
"@angular-devkit/build-angular:application": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["production", "^production"]
} }
}, },
"namedInputs": { "namedInputs": {
@@ -52,10 +57,10 @@
}, },
"generators": { "generators": {
"@nx/angular:application": { "@nx/angular:application": {
"style": "scss",
"linter": "eslint",
"unitTestRunner": "none",
"e2eTestRunner": "none", "e2eTestRunner": "none",
"linter": "eslint",
"style": "scss",
"unitTestRunner": "none",
"inlineStyle": "true", "inlineStyle": "true",
"inlineTemplate": "true", "inlineTemplate": "true",
"prefix": "app", "prefix": "app",

29983
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,7 @@
"@swc-node/register": "1.8.0", "@swc-node/register": "1.8.0",
"@swc/cli": "~0.1.62", "@swc/cli": "~0.1.62",
"@swc/core": "1.3.94", "@swc/core": "1.3.94",
"@tailwindcss/forms": "^0.5.7",
"@tanstack/angular-query-devtools-experimental": "^5.12.1", "@tanstack/angular-query-devtools-experimental": "^5.12.1",
"@testing-library/angular": "15.1.0", "@testing-library/angular": "15.1.0",
"@testing-library/cypress": "10.0.1", "@testing-library/cypress": "10.0.1",