Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Задание на проверку ТГ: @my4kish #82

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
Expand Down Expand Up @@ -90,12 +91,16 @@
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}
931 changes: 929 additions & 2 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
"private": true,
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/cdk": "^17.3.10",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/material": "^17.3.10",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@ngrx/store": "^17.2.0",
"@ngrx/store-devtools": "^17.2.0",
"normalize.css": "^8.0.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
Expand All @@ -35,4 +40,4 @@
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.4.2"
}
}
}
338 changes: 2 additions & 336 deletions src/app/app.component.html

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Header } from './header/header.component';

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
imports: [RouterOutlet, Header],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
Expand Down
15 changes: 13 additions & 2 deletions src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { ApplicationConfig } from '@angular/core';
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { provideStore } from '@ngrx/store';
import { userReducer } from './users-list/store/user.reducer';
import { provideStoreDevtools } from '@ngrx/store-devtools';

export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)]
providers: [
provideRouter(routes),
provideHttpClient(),
provideStore({
users: userReducer
}),
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() })
]
};
8 changes: 7 additions & 1 deletion src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { Routes } from '@angular/router';
import { UserListComponent } from './users-list/users-list.component';

export const routes: Routes = [];
export const routes: Routes = [
{
path: 'users',
component: UserListComponent,
}
];
7 changes: 7 additions & 0 deletions src/app/header/header.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<mat-toolbar class="header">
<span>Junior task</span>
<div class="nav">
<a mat-button href="http://localhost:4200">Главная</a>
<a mat-button href="http://localhost:4200/users">Пользователи</a>
</div>
</mat-toolbar>
14 changes: 14 additions & 0 deletions src/app/header/header.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.header{
display: flex;
gap: 220px;
box-shadow: 0 -6px 10px 5px rgba(0,0,0,0.5);
}

.nav{
display: flex;
gap: 20px;
}

.nav>a{
font-size: medium;
}
13 changes: 13 additions & 0 deletions src/app/header/header.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component } from "@angular/core";
import {MatToolbarModule} from '@angular/material/toolbar';
import { MatButtonModule } from "@angular/material/button";

@Component({
selector: 'header',
standalone: true,
templateUrl: './header.component.html',
styleUrl: './header.component.scss',
imports: [MatToolbarModule, MatButtonModule],
})
export class Header{
}
21 changes: 21 additions & 0 deletions src/app/local-storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { User } from './users-list/users-list.component';

@Injectable({
providedIn: 'root',
})
export class LocalStorageService {
getItem(key: string): User[] | null {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
}

setItem(key: string, data: object): void {
localStorage.setItem(key, JSON.stringify(data));
}

removeItem(key: string): boolean {
localStorage.removeItem(key);
return true;
}
}
11 changes: 11 additions & 0 deletions src/app/users-api.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class UsersApiService {
private readonly apiService = inject(HttpClient);

getUsers() {
return this.apiService.get('https://jsonplaceholder.typicode.com/users');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="dialog">
<h1>{{this.data.isEdit ? 'Редактирование пользователя' : 'Создание пользователя'}}</h1>
<form [formGroup] = "form" class="form">
<mat-form-field>
<mat-label>Имя</mat-label>
<input matInput class="form-input" formControlName="name" />
</mat-form-field>
<mat-form-field>
<mat-label>Почта</mat-label>
<input matInput class="form-input" formControlName="email" />
</mat-form-field>
<mat-form-field>
<mat-label>Вебсайт</mat-label>
<input matInput class="form-input" formControlName="website" />
</mat-form-field>
<mat-form-field>
<mat-label>Компания</mat-label>
<input matInput class="form-input" formControlName="companyName" />
</mat-form-field>
<button color="accent" mat-flat-button [mat-dialog-close]="form.value" [disabled]="form.invalid">{{this.data.isEdit ? 'Edit user' : 'Create user'}}</button>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

.dialog{
margin: 30px;
}

.form{
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Component, EventEmitter, inject, Output } from "@angular/core";
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from "@angular/material/dialog";
import {MatInputModule} from '@angular/material/input';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatButtonModule} from '@angular/material/button';

@Component({
selector:'app-create-user-dialog',
templateUrl:'./create-edit-user-dialog.component.html',
styleUrl:'./create-edit-user-dialog.component.scss',
imports:[ReactiveFormsModule, MatDialogClose, MatInputModule, MatFormFieldModule, MatButtonModule],
standalone: true,
})
export class CreateEditUserDialogComponent{
public readonly data = inject(MAT_DIALOG_DATA)
private readonly dialogRef = inject(MatDialogRef<CreateEditUserDialogComponent>);

@Output()
isEditable = new EventEmitter();

public form = new FormGroup({
name: new FormControl(this.data?.user?.name, [Validators.required]),
email:new FormControl(this.data?.user?.email, [Validators.required, Validators.email]),
website:new FormControl(this.data?.user?.website, [Validators.required, Validators.minLength(3)]),
companyName:new FormControl(this.data?.user?.company.name, [Validators.required]),
})

public editUser() {
this.dialogRef?.close(this.form.value)
}
}
33 changes: 33 additions & 0 deletions src/app/users-list/store/user.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createReducer, on } from '@ngrx/store';
import { UsersActions } from './users.actions';
import { User } from '../users-list.component';

const initialState: { users: User[] } = {
users: [],
};

export const userReducer = createReducer(
initialState,
on(UsersActions.set, (state, payload) => ({
...state,
users: payload.users,
})),
on(UsersActions.edit, (state, payload) => ({
...state,
users: state.users.map((user) => {
if (user.id === payload.user.id) {
return payload.user;
} else {
return user;
}
}),
})),
on(UsersActions.create, (state, payload) => ({
...state,
users: [...state.users, payload.user],
})),
on(UsersActions.delete, (state, payload) => ({
...state,
users: state.users.filter((user) => user.id !== payload.id),
}))
);
12 changes: 12 additions & 0 deletions src/app/users-list/store/users.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createActionGroup, props } from '@ngrx/store';
import { User } from '../users-list.component';

export const UsersActions = createActionGroup({
source: 'Users',
events: {
set: props<{ users: User[] }>(),
edit: props<{ user: User }>(),
create: props<{ user: User }>(),
delete: props<{ id: number }>(),
},
});
17 changes: 17 additions & 0 deletions src/app/users-list/store/users.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createSelector } from '@ngrx/store';
import { User } from '../users-list.component';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

удалите лишнюю строку

interface UserState {
users: User[];
}

interface AppState {
users: UserState;
}

export const selectUsersFeature = (state: AppState) => state.users;

export const selectUsers = createSelector(
selectUsersFeature,
(state: UserState) => state.users
);
16 changes: 16 additions & 0 deletions src/app/users-list/user-card/user-card.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<mat-card class="users-card">
<mat-card-header>
<mat-card-title>
<p>{{user.name}}</p>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>{{user.email}}</p>
<p>{{user.website}}</p>
<p>{{user.company.name}}</p>
</mat-card-content>
<mat-card-actions class="users-card-btns">
<button class="users-card-btn" mat-raised-button (click)="onUserEdit(user)">Редактировать юзера</button>
<button class="users-card-btn delete" mat-raised-button (click)="onDeleteUser(user.id)">Удалить юзера</button>
</mat-card-actions>
</mat-card>
19 changes: 19 additions & 0 deletions src/app/users-list/user-card/user-card.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

.users-card{
max-width: 400px;
}

.users-card-btns{
gap: 10px;
margin: 10px;
}

.users-card-btn.delete{
background-image: linear-gradient(147deg, #fe8a39 0%, #fd3838 74%);
color: whitesmoke;
}

.users-card-btn:disabled{
background-image: none;
color: grey;
}
30 changes: 30 additions & 0 deletions src/app/users-list/user-card/user-card.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import {MatCardModule} from '@angular/material/card';
import {MatButton} from '@angular/material/button';

@Component({
selector:'app-user-card',
templateUrl:'./user-card.component.html',
styleUrl:'./user-card.component.scss',
standalone:true,
imports: [MatCardModule, MatButton],
})
export class UserCardComponent{

@Input()
user:any

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

типизируйте, не стоит использовать any


@Output()
deleteUser = new EventEmitter()

@Output()
userEdit = new EventEmitter()

onDeleteUser(userId:number) {
this.deleteUser.emit(userId)
}

onUserEdit(user: any) {
this.userEdit.emit(user)
}
}
8 changes: 8 additions & 0 deletions src/app/users-list/users-list.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="users-list">
<button class="add-user-btn" mat-raised-button (click)="openDialog()">Добавить пользователя</button>
<div class="users-list-cards">
<div *ngFor="let item of users$ | async">
<app-user-card [user]="item" (deleteUser)="deleteUser($event)" (userEdit)="openDialog($event)"></app-user-card>
</div>
</div>
</div>
Loading