Compare commits

..

45 Commits

Author SHA1 Message Date
ludovic.castglia 345947b27e readme
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 4b8bdf0032 fix commentaire index
8 months ago
Ludovic CASTIGLIA cbe7a867a3 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 7bf9605ced re
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia a056b90a8f e
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 89f7620cb8 j
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 3bb534ed46 e
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 209543d823 ter
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 69aa0aaf48 ters
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 9b8fb3293c mb
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia b8bc73975a ez
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia cd593e4c40 a
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 678e7f0222 e
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia e3ad6c880f oui
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 4bc7673c9e a
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia f2c83d75c9 pas de vite mais on sais jamais
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia baf7f04c63 c'est bon
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 9a42f3f272 baseUrl compilerOtion > tsconfig.json
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia b2eac0d3ce hotfix
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia de65f8f14b base href
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia d6dd2dbfc9 mauvaise ip
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia a49b674b1d le htaccess mais aucun sens
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 79eb462ab9 host ? mais je n'y trust pas
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 08845e8331 localhost?
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia ca730fd8c9 wrong port
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia 5f9b105a26 ha
continuous-integration/drone/push Build is passing Details
8 months ago
ludovic.castglia ca95a0c8f2 ?
8 months ago
ludovic.castglia aabcb213ac mauvais nom
continuous-integration/drone/push Build is failing Details
8 months ago
ludovic.castglia df8e38af69 cicd
continuous-integration/drone/push Build is failing Details
8 months ago
ludovic.castglia 8c4c581ecc encore
continuous-integration/drone/push Build is failing Details
8 months ago
ludovic.castglia 3b9aedc1bb modi
continuous-integration/drone/push Build is failing Details
8 months ago
ludovic.castglia 102a571b6e cicd
continuous-integration/drone/push Build is failing Details
8 months ago
ludovic.castglia b669ee07fd merge master
8 months ago
ludovic.castglia f9483a34fb test drone
8 months ago
ludovic.castglia 647f80d075 a
8 months ago
Raphael LACOTE 12ac9ee20d Mise en place du localStorage dans User Service
8 months ago
Raphael LACOTE 76b98e2328 Supression de toutes les références à des fichiers Book
8 months ago
Raphael LACOTE 1dc47e6b7a Ajouts de styles et améliorations de code
8 months ago
Raphael LACOTE 1af748e6f1 Ajout de la navbar
8 months ago
Raphael LACOTE 6395a54999 Merge de la brange fait à la mano
8 months ago
ludovic.castglia 652b1925d6 le mode easy était trop simple pour le coup
8 months ago
ludovic.castglia ecc29e9ff6 oui
8 months ago
ludovic.castglia d7a8a32be0 ajout fonctionalité
8 months ago
ludovic.castglia 5404192301 modifi
8 months ago
ludovic.castglia e0c256d835 ajout logique du sudoku
8 months ago

@ -0,0 +1,30 @@
kind: pipeline
type: docker
name: Deploiement
trigger:
event:
- push
steps:
- name: docker-push
image: plugins/docker
settings:
dockerfile: docker/Dockerfile
context: .
registry: hub.codefirst.iut.uca.fr
mirror: https://proxy.iut.uca.fr:8443
repo: hub.codefirst.iut.uca.fr/ludovic.castiglia/angular_minigame
username:
from_secret: SECRET_REGISTRY_USERNAME
password:
from_secret: SECRET_REGISTRY_PASSWORD
- name: deploy-container
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: hub.codefirst.iut.uca.fr/ludovic.castiglia/angular_minigame:latest
CONTAINERNAME: sudoku
COMMAND: create
OVERWRITE: true

@ -4,4 +4,10 @@ by
```
Raphael LACOTE
Ludovic CASTIGLIA
```
Pour lancer le projet, Vous pouvez uttiliser le dockerfile présent dans ```./docker/Dockerfile```. Pour cela, vous devez bien sur modifier l'url de base dans le index.html et également modifier dans ```angular.json``` les deux lignes qui configure le ```baseHref``` suivant:
```
"baseHref": "/containers/ludoviccastiglia-sudoku/",
```

@ -32,6 +32,7 @@
},
"configurations": {
"production": {
"baseHref": "/containers/ludoviccastiglia-sudoku/",
"budgets": [
{
"type": "initial",
@ -47,6 +48,7 @@
"outputHashing": "all"
},
"development": {
"baseHref": "/containers/ludoviccastiglia-sudoku/",
"optimization": false,
"extractLicenses": false,
"sourceMap": true

@ -0,0 +1,4 @@
<Files *>
Order Allow,Deny
Deny from All
</Files>

@ -0,0 +1,13 @@
FROM node:alpine
WORKDIR /usr/src/app
COPY . /usr/src/app
RUN npm install -g @angular/cli
RUN npm install
EXPOSE 80
CMD ["ng", "serve", "--host", "0.0.0.0", "--port", "80"]

@ -1,3 +1,3 @@
<!-- <app-book-menu></app-book-menu> -->
<app-book-menu></app-book-menu>
<router-outlet (activate)="onActivate($event)"></router-outlet>

@ -2,45 +2,41 @@ import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { BookFormComponent } from './components/book-form/book-form.component';
import { BookListComponent } from './components/book-list/book-list.component';
import { LoginComponent } from './components/login/login.component';
import { UserService } from './services/user-service';
import { User } from './models/user.model';
import { BookService } from './services/book-service';
import { Book } from './models/book.model';
import { UserMenuComponent } from './components/user-menu/user-menu.component';
import { SudokuService } from "./services/sudoku-service"
import { UserAccueilComponent } from './components/user-accueil/user-accueil.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, BookListComponent, BookFormComponent, UserMenuComponent, HttpClientModule],
imports: [RouterOutlet, UserMenuComponent, HttpClientModule],
templateUrl: './app.component.html',
providers: [
BookService,
UserService,
SudokuService,
SudokuService
]
})
export class AppComponent {
title = 'angular-tp2-correct';
constructor(protected bookService: BookService, protected userService: UserService, protected sudokuService: SudokuService){ }
addBook($event: Book): void {
this.bookService.addBook($event);
}
constructor(protected userService: UserService, protected sudokuService: SudokuService){ }
changeUserName($event: User): void {
this.userService.addToLocalStorage($event);
}
onActivate(componentRef: Event): void {
console.log('onActivate', componentRef);
if(componentRef instanceof BookFormComponent) {
componentRef.addBookEvent.subscribe((newBook: Book) => {
this.addBook(newBook);
if(componentRef instanceof LoginComponent) {
componentRef.addConnectEvent.subscribe((connectedUser: User) => {
this.changeUserName(connectedUser);
});
}
}
}

@ -1,21 +1,17 @@
import { Routes } from '@angular/router';
import { BookListComponent } from './components/book-list/book-list.component';
import { BookFormComponent } from './components/book-form/book-form.component';
import { BookDetailComponent } from './components/book-detail/book-detail.component';
import { LoginComponent } from './components/login/login.component';
import { UserListComponent } from './components/user-list/user-list.component';
import { UserDetailComponent } from './components/user-detail/user-detail.component';
import { SudokuComponent } from './components/sudoku/sudoku.component';
import { UserAccueilComponent } from './components/user-accueil/user-accueil.component';
import { DifficultySelectorComponent } from './components/difficulty-selector/difficulty-selector.component'
import { UserAccueilComponent } from './components/user-accueil/user-accueil.component';
export const routes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'books', component: BookListComponent },
{ path: 'book/add', component: BookFormComponent },
{ path: 'book/:id', component: BookDetailComponent },
{ path: 'accueil', component: UserAccueilComponent },
{ path: 'users', component: UserListComponent },
{ path: 'accueil', component: UserAccueilComponent },
{ path: 'user/:id', component: UserDetailComponent },
{ path: 'sudoku', component: DifficultySelectorComponent },
{ path: 'sudoku/:difficulty', component: SudokuComponent },
{ path: '**', redirectTo: '', pathMatch: 'full' }
];

@ -1,12 +0,0 @@
<div *ngIf="selectedBook">
<h2>{{selectedBook.title | uppercase}}</h2>
<div><span>id: </span>{{selectedBook.id}}</div>
<div>
<label for="book-author">Auteur : </label>
{{ selectedBook.author }}
</div>
<div>
<label for="book-publicationDate">Date de publication : </label>
{{ selectedBook.publicationDate | date: 'EEEE d MMMM y' }}
</div>
</div>

@ -1,25 +0,0 @@
import { Component} from '@angular/core';
import { UpperCasePipe, DatePipe, NgIf} from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { Book } from '../../models/book.model';
import { BookService } from '../../services/book-service';
@Component({
standalone: true,
selector: 'app-book-detail',
imports: [DatePipe, UpperCasePipe, NgIf],
templateUrl: './book-detail.component.html',
styles: ``
})
export class BookDetailComponent {
selectedBook!: Book | undefined;
constructor(protected bookService: BookService, private activatedRoute: ActivatedRoute) {}
ngOnInit(){
const id = this.activatedRoute.snapshot.params['id'];
this.selectedBook = this.bookService.getBookById(id);
console.log(this.selectedBook);
}
}

@ -1,32 +0,0 @@
<section>
<h2>Add book form</h2>
<form [formGroup]="bookForm">
<div>
<mat-form-field class="full-width">
<mat-label>Title</mat-label>
<input matInput type="text" formControlName="title" />
</mat-form-field>
</div>
<div>
<mat-form-field class="full-width">
<mat-label>Author</mat-label>
<input matInput type="text" formControlName="author" />
</mat-form-field>
</div>
<div>
<mat-form-field class="full-width">
<mat-label>Publication date</mat-label>
<input matInput [matDatepicker]="picker" type="text" formControlName="publicationDate" />
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<div>
<button mat-flat-button color="primary" type="button" (click)="addBook()">Add</button>
</div>
</form>
</section>

@ -1,49 +0,0 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { Book } from '../../models/book.model';
import { MatButtonModule } from '@angular/material/button';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
@Component({
selector: 'app-book-form',
standalone: true,
imports: [
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatMenuModule,
MatDatepickerModule,
MatNativeDateModule,
FormsModule,
ReactiveFormsModule
],
templateUrl: './book-form.component.html'
})
export class BookFormComponent {
@Output() addBookEvent = new EventEmitter<Book>();
book: Book = { id: 0, title: '', author: '', publicationDate: new Date() }
bookForm: FormGroup = new FormGroup({
title: new FormControl(this.book.title, Validators.required),
author: new FormControl(this.book.author, Validators.required),
publicationDate: new FormControl(this.book.publicationDate, Validators.required)
});
addBook() {
if (this.bookForm.invalid) {
console.log("ERREUR");
return;
}
this.book = this.bookForm.value;
this.addBookEvent.emit(this.book);
this.bookForm.reset();
}
}

@ -1 +0,0 @@
<h1>Bienvenue sur la page d'accueil du Book Shop</h1>

@ -1,12 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-book-home',
standalone: true,
imports: [],
templateUrl: './book-home.component.html',
styles: ``
})
export class BookHomeComponent {
}

@ -1,7 +0,0 @@
<h2>Book list</h2>
<ul>
<li *ngFor="let book of books">
<a routerLink="/book/{{ book.id }}">{{ book.title }}</a>
</li>
</ul>

@ -1,23 +0,0 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NgFor, DatePipe } from '@angular/common';
import { Book } from '../../models/book.model';
import { BookService } from '../../services/book-service'
@Component({
selector: 'app-book-list',
standalone: true,
imports: [NgFor, DatePipe, RouterModule],
templateUrl: './book-list.component.html'
})
export class BookListComponent {
books: Book[] = [];
constructor(protected bookService: BookService){
}
ngOnInit(){
this.books = this.bookService.getAll();
}
}

@ -0,0 +1,21 @@
section {
min-height: 50%;
min-width: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border: 1px solid #000;
border-radius: 50px;
text-align: center;
}
section .btn {
position: absolute;
top: 0%;
height: 100%;
width: calc(100% - 1em);
display: flex;
align-items: center;
justify-content: space-evenly;
}

@ -0,0 +1,11 @@
<section>
<h1>
Choisir une difficulté:
</h1>
<div class="btn">
<button routerLink="/sudoku/easy" mat-flat-button color="primary">easy</button>
<button routerLink="/sudoku/medium" mat-flat-button color="primary">medium</button>
<button routerLink="/sudoku/hard" mat-flat-button color="primary">hard</button>
</div>
</section>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DifficultySelectorComponent } from './difficulty-selector.component';
describe('DifficultySelectorComponent', () => {
let component: DifficultySelectorComponent;
let fixture: ComponentFixture<DifficultySelectorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DifficultySelectorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DifficultySelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'app-difficulty-selector',
standalone: true,
imports: [RouterModule,MatButtonModule],
templateUrl: './difficulty-selector.component.html',
styleUrl: './difficulty-selector.component.css'
})
export class DifficultySelectorComponent {
}

@ -1,4 +1,4 @@
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
@ -10,7 +10,6 @@ import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { User } from '../../models/user.model';
import { NgIf } from '@angular/common';
import { UserService } from '../../services/user-service';
import { Router } from '@angular/router';
@Component({
@ -35,7 +34,6 @@ export class LoginComponent implements OnInit {
@Output() addConnectEvent = new EventEmitter<User>();
public isButtonVisible = false;
private UserService : UserService;
user: User = {id:1, login: '', password: '', streak: null, streaks: [], points: null}
loginForm: FormGroup = new FormGroup({
@ -43,8 +41,7 @@ export class LoginComponent implements OnInit {
password: new FormControl(this.user.password, Validators.required),
});
public constructor(us: UserService,private router: Router) {
this.UserService = us
public constructor(private router: Router) {
}
ngOnInit() {
@ -61,13 +58,10 @@ export class LoginComponent implements OnInit {
console.log("ERREUR DIFFERENT");
return;
}
console.log(this.loginForm.value.login)
this.user = this.loginForm.value;
localStorage.setItem('UserConnecte',String(this.user.login));
//this.UserService.addUserToLocal(this.user);
this.addConnectEvent.emit(this.user)
this.addConnectEvent.emit(this.loginForm.value)
this.loginForm.reset();
this.isButtonVisible = false;
this.router.navigate(['/accueil']);

@ -1,29 +1,17 @@
:host {
--cellSize: 50px;
--color1: #f00;
--color2: #0f0;
--color3: #00f;
--color4: #ff0;
--color5: #f0f;
--color6: #0ff;
--color7: #f77;
--color8: #7f7;
--color9: #77f;
}
.grille {
position: absolute;
left: 50%;
transform: translate(-50%);
--color: #ddd;
--color-faux: rgba(255,0,0,0.4);
--bc-win: #ff0;
--bc-selec: rgba(0,0,255,0.2);
}
/* on reset l'apparence des input*/
.cell::-webkit-outer-spin-button,
.cell::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
.cell[type=number] {
-moz-appearance: textfield;
}
@ -33,44 +21,128 @@
width: var(--cellSize);
text-align: center;
outline: none;
background-color: transparent;
}
.grid, .subgrid {
display: grid;
height: max-content;
width: max-content;
}
.grille {
display: flex;
.sub0, .sub1, .sub2, .y0, .y1, .y2 {
grid-row: 1/2;
}
.sub3 , .sub4, .sub5, .y3, .y4, .y5{
grid-row: 2/3;
}
.column:nth-child(-n+9) div:nth-child(-n+9) {
border: solid 1px var(--color1);
.sub6 , .sub7, .sub8, .y6, .y7, .y8 {
grid-row: 3/4;
}
.column:nth-child(-n+9) div:nth-child(-n+6) {
border: solid 1px var(--color2);
.sub0, .sub3, .sub6, .y0, .y3, .y6 {
grid-column: 1/2;
}
.column:nth-child(-n+9) div:nth-child(-n+3) {
border: solid 1px var(--color3);
.sub1, .sub4, .sub7, .y1, .y4, .y7 {
grid-column: 2/3;
}
.column:nth-child(-n+6) div:nth-child(-n+9) {
border: solid 1px var(--color4);
.sub2, .sub5, .sub8, .y2, .y5, .y8 {
grid-column: 3/4;
}
.column:nth-child(-n+6) div:nth-child(-n+6) {
border: solid 1px var(--color5);
.sub1, .sub3, .sub5, .sub7 {
background-color: var(--color);
}
.column:nth-child(-n+6) div:nth-child(-n+3) {
border: solid 1px var(--color6);
.faux {
background-color: var(--color-faux);
}
.column:nth-child(-n+3) div:nth-child(-n+9) {
border: solid 1px var(--color7);
input:disabled {
color: #000;
}
.column:nth-child(-n+3) div:nth-child(-n+6) {
border: solid 1px var(--color8);
.winpopup {
min-width: 50%;
min-height: 50%;
background-color: var(--bc-win);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border-radius: 50px;
}
.selectedRowCol {
background-color: var(--bc-selec);
}
.column:nth-child(-n+3) div:nth-child(-n+3) {
border: solid 1px var(--color9);
input:disabled {
pointer-events: none;
}
.main {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
min-height: 50vh;
min-width: 70vw;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.haut {
position: relative;
width: 100%;
height: max-content;
display: flex;
justify-content: center;
align-items: center;
}
.button {
margin-left: 20px;
position: relative;
min-height: 40vh;
width: 100%;
height: 100%;
display: flex;
justify-content: space-evenly;
align-items: center;
flex-direction: column;
}
.grid {
min-height: 40vh;
}
.bas {
position: relative;
padding-top: 30px;
width: 100%;
display: flex;
justify-content: space-evenly;
align-items: center;
}
.bas button {
padding: 20px;
aspect-ratio: 1/1;
text-align: center;
}
.bas button div {
position: relative;
top: -50%;
left: 200%;
}

@ -1,21 +1,42 @@
<app-book-menu></app-book-menu>
<!-- <app-book-menu></app-book-menu> -->
<p>sudoku works!</p>
<section class="main">
<div class="grille">
<div *ngFor="let row of grille" class="column">
<div *ngFor="let cell of row" class="row">
<input *ngIf="cell != 0" type="number" value="{{cell}}" class="cell" max="9" min="0">
<input *ngIf="cell == 0" type="number" value="" class="cell" max="9" min="0">
<div class="haut">
<div class="grid">
<div *ngFor="let row of grille; let x = index" class="subgrid sub{{x}}">
<div *ngFor="let cell of row; let y = index" class="case x{{x}} y{{y}}">
<input *ngIf="cell != 0" type="number" value="{{cell}}" class="cell" max="9" min="0" id="xy{{x}}{{y}}" (change)="key()" (click)="colorSameRowCol(y,x,$event)">
<input *ngIf="cell == 0" type="number" value="" class="cell" max="9" min="0" id="xy{{x}}{{y}}" (click)="colorSameRowCol(y,x,$event)" (change)="key()">
</div>
</div>
</div>
<div class="button">
<button (click)="publish()">
soumettre
</button>
<button (click)="corriger()">
verrifier
</button>
<p>nombre d'indice: {{nbIndice}}</p>
</div>
</div>
</div>
<div class="bas">
<button *ngFor="let obj of [].constructor(9); let i = index" (click)="addNumber(i+1)">{{i+1}}<div id="btn{{i+1}}">{{nbEachNumber[i]}}</div></button>
</div>
<button (click)="publish()">
valider la grille
</button>
</section>
<button (click)="corriger()">
corriger la grille
</button>
<section *ngIf="haveWin" class="winpopup">
<h2>Vous avez gagné !</h2>
<div class="menu">
<button routerLink="/">
retour à la page d'accueil
</button>
<button routerLink="/sudoku/">
nouvelle partie
</button>
</div>
</section>

@ -2,26 +2,80 @@ import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NgFor,NgIf } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { UserMenuComponent } from '../user-menu/user-menu.component';
import { UserMenuComponent } from '../user-menu/user-menu.component';
import { SudokuService } from '../../services/sudoku-service';
import { Block } from '@angular/compiler';
@Component({
selector: 'app-sudoku',
standalone: true,
imports: [UserMenuComponent, RouterModule, NgFor, NgIf],
imports: [RouterModule, NgFor, NgIf,UserMenuComponent],
templateUrl: './sudoku.component.html',
styleUrl: './sudoku.component.css'
})
export class SudokuComponent {
grille: number[][] = [];
nbIndice: number = 0;
haveWin: boolean = false;
nbEachNumber:number[] = [0,0,0,0,0,0,0,0,0];
lastInputClicked: HTMLInputElement | undefined;
constructor(protected sudokuService: SudokuService, private activatedRoute: ActivatedRoute) {
}
ngOnInit() {
const difficulty = this.activatedRoute.snapshot.params["difficulty"];
this.Request(difficulty);
}
public publish() {
this.resetError();
this.haveWin = this.sudokuService.publishGame(this.getGrid(),"pas test",this.nbIndice)
if (this.haveWin) return;
this.corriger();
}
public corriger() {
this.resetError();
if (this.sudokuService.isCorrect(this.getGrid())) {
this.publish();
return;
}
this.nbIndice += 1;
let error: number[][] = this.sudokuService.validationGrille(this.getGrid());
for (let y=0; y<error.length; y++) {
for (let x=0; x<error[y].length; x++) {
if (error[y][x] == -1) {
this.marqueError(x,y);
}
}
}
}
private getGrid():number[][] {
let grid: number[][] = [];
for (let y=0; y<9; y++) {
grid.push([]);
for (let x=0; x<9; x++) {
grid[y].push(Number((document.getElementById("xy"+y.toString()+x.toString()) as HTMLInputElement).value) | 0);
}
}
return grid;
}
private marqueError(x:number,y:number) {
let error = document.getElementById("xy"+y.toString()+x.toString());
if (error == null) return;
error.classList.add("faux");
}
private resetError() {
let errors = document.getElementsByClassName("faux");
for (let i=0; i<errors.length; i++) {
errors[i].classList.remove("faux");
}
}
private Request(difficulty: string) {
switch (difficulty) {
case 'easy':
this.grille = this.sudokuService.getEasy();
@ -36,12 +90,61 @@ export class SudokuComponent {
this.grille = this.sudokuService.getEasy();
break;
}
console.log(this.grille);
if (this.grille.length == 0) {
setTimeout(()=> {this.Request(difficulty)},100);
return;
}
this.updateNb();
}
public colorSameRowCol(a:number ,b:number, event: Event) {
if (event.target != null) this.lastInputClicked = <HTMLInputElement>event.target;
this.resetColor();
let xp:number = (a%3)*3+ b%3;
let yp:number = Math.floor(a/3)*3+Math.floor(b/3);
for(let x:number=0; x<9; x++) {
for(let y:number=0; y<9; y++) {
if (xp == x || yp == y) {
let newx:number = (y%3)*3+(x%3);
let newy:number = Math.floor(y/3)*3+Math.floor(x/3);
document.getElementById("xy"+newx.toString()+newy.toString())?.classList.add("selectedRowCol")
}
}
}
}
public publish() {
public resetColor() {
this.updateNb();
let inputs:HTMLCollection = document.getElementsByClassName("selectedRowCol");
for (let i:number=0;inputs.length>0;i++) {
inputs[0].classList.remove("selectedRowCol");
}
}
public corriger() {
private updateNb() {
for(let i:number=1;i<10;i++) {
this.nbEachNumber[i-1] = this.nb(i);
}
}
private nb(i:number) {
let a:number=0
this.grille.forEach(e=>{
a += e.filter((v) => (v == i)).length
})
return 9-a;
}
public key() {
this.grille = this.getGrid();
this.updateNb();
}
public addNumber(nb:number) {
this.resetColor();
if (this.nbEachNumber[nb-1] <= 0) return;
if (this.lastInputClicked == null) return;
this.lastInputClicked.value = nb.toString();
this.key();
}
}

@ -1,23 +1,17 @@
<app-book-menu></app-book-menu>
<h2>{{formattedDate}}</h2>
<table>
<tbody>
<tr>
<th>LUN</th>
<th>MAR</th>
<th>MER</th>
<th>JEU</th>
<th>VEN</th>
<th>SAM</th>
<th>DIM</th>
</tr>
<th *ngFor="let d of day">
{{ d }}
</th>
</tr>
<tr>
<th *ngFor="let azerty of streaks">
{{ azerty }}
<th *ngFor="let streak of streaks">
{{ streak }}
</th>
</tr>
</tr>
</tbody>
</table>

@ -3,37 +3,33 @@ import { RouterModule } from '@angular/router';
import { UserMenuComponent } from '../user-menu/user-menu.component';
import { UserService } from '../../services/user-service';
import { NgFor } from '@angular/common';
import { DatePipe } from '@angular/common';
interface Streak {
day: string
isPlayed: boolean
}
}
@Component({
selector: 'app-user-accueil',
standalone: true,
imports: [UserMenuComponent,RouterModule,NgFor],
imports: [UserMenuComponent,RouterModule,NgFor,DatePipe],
templateUrl: './user-accueil.component.html',
styleUrl: './user-accueil.component.css'
})
export class UserAccueilComponent {
protected formattedDate : String
protected streaks: Object[]
protected streaks: Object[]
protected day: String[]
public constructor(us : UserService) {
const currentDate: Date = new Date
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric'
};
this.formattedDate = currentDate.toLocaleDateString(undefined, options)
this.formattedDate = this.getDate()
this.streaks = ["❌","❌","❌","❌","❌","❌","❌"]
this.day = ["L","M","M","J","V","S","D"]
let userConnecte = localStorage.getItem('UserConnecte')
let userConnecte = us.getItemUserConnecte()
if(userConnecte == undefined) {return}
let id = us.getIdByUser(userConnecte)
@ -43,16 +39,27 @@ export class UserAccueilComponent {
if(streaks == undefined) {return}
this.streaks = []
this.day = []
streaks.forEach(element => {
let a : Streak = element as Streak
this.day.push(a["day"])
if(a["isPlayed"]){
this.streaks.push("✅")
} else {
this.streaks.push("❌")
}
});
});
}
public getDate(): String{
const currentDate: Date = new Date
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric'
}
return currentDate.toLocaleDateString(undefined, options)
}
}

@ -0,0 +1,40 @@
section {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
min-height: 50vh;
min-width: 50vw;
}
thead, tr {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
thead {
border-bottom: 1px solid #000;
}
th, td {
position: relative;
width: 100%;
text-align: center;
}
tbody {
width: 100%;
display: block;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
}
.pagination > * {
margin: 20px;
}

@ -1,27 +1,29 @@
<app-book-menu></app-book-menu>
<!-- <app-book-menu></app-book-menu> -->
<h2>User list</h2>
<section>
<thead>
<tr>
<th scope="col">Pseudo</th>
<th scope="col" (click)="changeWatchedvar('nbPts')">Nombre de points</th>
<th scope="col" (click)="changeWatchedvar('streak')">Streak</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<td>{{user.login}}</td>
<td>{{user.points}}</td>
<td>{{user.streak}}</td>
</tr>
</tbody>
<div class="pagination">
<button (click)="loadPage(-1)">
<
</button>
<p>{{nbPage}}/{{userService.getNbPage()}}</p>
<button (click)="loadPage(1)">
>
</button>
</div>
<thead>
<tr>
<th scope="col">Pseudo</th>
<th scope="col" (click)="changeWatchedvar('nbPts')">Nombre de points</th>
<th scope="col" (click)="changeWatchedvar('streak')">Streak</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<td >{{user.login}}</td>
<td>{{user.points}}</td>
<td>{{user.streak}}</td>
</tr>
</tbody>
<br>
<button (click)="loadPage(-1)">
<
</button>
<button (click)="loadPage(1)">
>
</button>
</section>

@ -5,6 +5,7 @@ import { NgFor } from '@angular/common';
import { User } from '../../models/user.model';
import { UserService } from '../../services/user-service';
import { UserMenuComponent } from '../user-menu/user-menu.component';
import { DataSource } from '@angular/cdk/collections';
@Component({
selector: 'app-user-list',
@ -25,9 +26,7 @@ export class UserListComponent {
ngOnInit() {
this.nbPage = 1;
this.users = this.userService.getAll();
this.loadPage(0);
console.log(this.users);
}
public changeWatchedvar(newVar:string) {
@ -38,7 +37,7 @@ export class UserListComponent {
}
public loadPage(nb: number) {
if (this.nbPage + nb > this.userService.getNbPage() || this.nbPage + nb <= 0) {return;}
if ((this.nbPage + nb > this.userService.getNbPage() && this.users.length != 0) || this.nbPage + nb <= 0) {return;}
this.nbPage += nb;
switch(this.sortBy) {
case 'nbPts':
@ -51,5 +50,7 @@ export class UserListComponent {
this.users = this.userService.getUserNbStreak(this.nbPage,this.sortSens);
break;
}
if (this.users.length == 0) {setTimeout(()=>{this.nbPage -=nb;this.loadPage(nb)},100)};
}
}

@ -0,0 +1,33 @@
nav[mat-tab-nav-bar] {
display: flex;
justify-content: space-between;
background-color: #333;
padding: 10px 0
}
mat-tab-link, button[mat-button] {
flex: 1;
text-align: center;
color: white;
background-color: #444;
border: none;
padding: 15px;
cursor: pointer;
transition: background-color 0.3s;
}
mat-tab-link:hover, button[mat-button]:hover {
background-color: #555;
}
a[mat-menu-item] {
text-align: center;
padding: 10px;
display: block;
color: #111;
}
a[mat-menu-item]:hover {
background-color: #555;
}

@ -1,8 +1,13 @@
<button mat-button [matMenuTriggerFor]="menu">Menu</button>
<mat-menu #menu="matMenu">
<a mat-menu-item routerLink="/" routerLinkActive="active" ariaCurrentWhenActive="page">{{userconnecte}}</a>
<a mat-menu-item routerLink="/accueil" routerLinkActive="active" ariaCurrentWhenActive="page">Accueil</a>
<a mat-menu-item routerLink="/users" routerLinkActive="active" ariaCurrentWhenActive="page">Classement</a>
<a mat-menu-item routerLink="{{route}}" routerLinkActive="active" ariaCurrentWhenActive="page">Details</a>
<a mat-menu-item routerLink="/sudoku/2" routerLinkActive="active" ariaCurrentWhenActive="page">Sudoku</a>
<a mat-menu-item routerLink="/sudoku" routerLinkActive="active" ariaCurrentWhenActive="page">Sudoku</a>
<a mat-menu-item>Morpion</a>
<a mat-menu-item>Jeu de l'oie</a>
<a mat-menu-item>Star Wars Rebellion</a>
</mat-menu>
<nav mat-tab-nav-bar>
<mat-tab-link (click)="removePseudo()" label="userconnecte" [routerLink]="['/']" routerLinkActive="active" ariaCurrentWhenActive="page">{{userconnecte}}</mat-tab-link>
<mat-tab-link (click)="reloadPseudo()" label="Accueil" [routerLink]="['/accueil']" >Accueil</mat-tab-link>
<mat-tab-link (click)="reloadPseudo()" label="Classement" [routerLink]="['/users']" >Classement</mat-tab-link>
<button mat-button [matMenuTriggerFor]="menu">Menu</button>
</nav>

@ -3,35 +3,49 @@ import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { UserService } from '../../services/user-service';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterLink } from '@angular/router';
import { MatTabNav } from '@angular/material/tabs';
@Component({
selector: 'app-book-menu',
standalone: true,
imports: [RouterModule, MatButtonModule, MatMenuModule],
templateUrl: './user-menu.component.html'
imports: [RouterModule, MatButtonModule, MatMenuModule,MatTabsModule,RouterLink,MatTabNav],
templateUrl: './user-menu.component.html',
styleUrl: './user-menu.component.css'
})
export class UserMenuComponent {
private Service : UserService;
private service : UserService;
public userconnecte : String | undefined;
public route : String | undefined;
public constructor(us: UserService) {
this.Service = us
this.service = us
this.reloadPseudo()
}
public reloadPseudo(){
let id = localStorage.getItem('UserConnecte');
let name = this.service.getItemUserConnecte();
if (typeof name === "string" && name !="undefined"){
this.userconnecte = name
// let id : number | undefined = this.service.getIdByUser(name)
// if(id == undefined) return
// this.route = "/user/" + id
if (typeof id === "string" && id !="undefined"){
let user = this.Service.getUserById(+id)
this.route = "/users/" + user?.id
this.userconnecte = user?.login
this.userconnecte = id
} else {
this.userconnecte = "Pseudo";
this.route = "/users/0";
this.userconnecte = "Connexion";
this.route = "/user/0";
}
}
public removePseudo(){
localStorage.clear();
this.userconnecte = "Connexion";
}
}

@ -1,11 +0,0 @@
import { Book } from "../models/book.model";
export const BOOKS: Book[] = [
{ id: 1, title: 'The Lord of the Rings - The Fellowship of the Ring', author: 'J.R.R. Tolkien', publicationDate: new Date('07/29/1954') },
{ id: 2, title: 'The Lord of the Rings - The Two Towers', author: 'J.R.R. Tolkien', publicationDate: new Date('11/11/1954') },
{ id: 3, title: 'The Lord of the Rings - The Return of the King', author: 'J.R.R. Tolkien', publicationDate: new Date('10/20/1955') },
{ id: 4, title: 'Dune', author: 'Frank Herbert', publicationDate: new Date('1965') },
{ id: 5, title: 'Dune Messiah', author: 'Frank Herbert', publicationDate: new Date('1969') },
{ id: 6, title: 'It', author: 'Stephen King', publicationDate: new Date('09/15/1986') },
{ id: 7, title: 'Do Androids Dream of Electric Sheep?', author: 'Philip K. Dick', publicationDate: new Date('1968') }
];

@ -1,50 +0,0 @@
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from "@angular/core";
import { BOOKS } from "../datas/books.stub";
import { Book } from "../models/book.model";
@Injectable()
export class BookService {
private books: Book[];
private readonly bookApiUrl = 'https://66e8848bb17821a9d9dcf68c.mockapi.io/books';
public constructor(private http: HttpClient){
this.books = BOOKS;
this.http.get<Book[]>(this.bookApiUrl).subscribe(books => {
books.forEach(b => {
this.addBookToLocal(b);
});
});
}
public getAll(): Book[]{
return this.books;
}
public getBookById(id: number): Book | undefined{
return this.books.find(b => b.id === id);
}
public addBook(book: Book): void{
this.addBookToLocal(book);
this.addBookToApi(book);
}
private addBookToLocal(book: Book): void{
//console.log('addBookToLocal', book);
if(book.id === 0){
book.id = Math.max(...this.books.map(b => b.id)) + 1;
}
const existedBook = this.books.find(b => b.title === book.title || b.id === Number(book.id));
if(existedBook)
return;
this.books.push(book);
}
private addBookToApi(book: Book): void{
this.http.post<Book>(this.bookApiUrl, book).subscribe();
}
}

@ -1,13 +1,21 @@
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from "@angular/core";
interface apiResponseInterface {
data: number[][],
easy: number[][],
medium: number[][],
hard: number[][]
}
@Injectable()
export class SudokuService {
private correction: number[][];
private easy: number[][];
private medium: number[][];
private hard: number[][];
private readonly userApiUrl = 'https://sudoku-game-and-api.netlify.app/api/sudoku';
private readonly sudokuApiUrl = 'https://664ba07f35bbda10987d9f99.mockapi.io/api/sudoku/1';
private readonly saveGameApiUrl = 'https://664ba07f35bbda10987d9f99.mockapi.io/api/game';
public constructor(private http: HttpClient){
this.correction = [];
@ -15,18 +23,12 @@ export class SudokuService {
this.medium = [];
this.hard = [];
this.http.get<string>(this.userApiUrl).subscribe(apiResponse => {
let jsonResponse = JSON.parse(apiResponse);
this.correction = jsonResponse["data"];
this.easy = jsonResponse["easy"];
this.medium = jsonResponse["medium"];
this.hard = jsonResponse["hard"];
this.http.get<apiResponseInterface>(this.sudokuApiUrl).subscribe(apiResponse => {
this.correction = this.transformFormat(apiResponse["data"]);
this.easy = this.transformFormat(apiResponse["easy"]);
this.medium = this.transformFormat(apiResponse["medium"]);
this.hard = this.transformFormat(apiResponse["hard"]);
});
this.correction = [[3,8,7,1,2,6,5,4,9],[2,1,6,9,4,5,8,7,3],[5,9,4,3,8,7,6,2,1],[4,2,8,5,9,1,7,3,6],[7,5,1,2,6,3,9,8,4],[6,3,9,8,7,4,1,5,2],[1,7,5,6,3,2,4,9,8],[9,4,3,7,1,8,2,6,5],[8,6,2,4,5,9,3,1,7]];
this.easy = [[3,8,0,1,2,0,5,0,9],[2,1,6,9,0,0,8,0,0],[5,9,4,3,8,0,0,0,1],[4,2,8,5,0,0,7,3,6],[7,5,1,2,6,3,9,0,4],[0,3,0,0,0,0,1,5,2],[1,7,5,6,3,0,4,9,0],[9,4,3,0,1,8,0,0,5],[8,6,0,4,5,0,3,1,7]];
this.medium = [[3,0,0,0,0,0,0,4,0],[0,1,6,0,4,5,0,0,3],[0,9,0,3,8,0,0,2,0],[4,2,0,5,0,0,0,3,6],[7,5,0,2,6,0,0,8,4],[0,0,0,0,0,0,0,5,0],[1,0,5,6,3,0,0,9,8],[0,0,0,7,1,8,0,6,5],[0,0,2,4,0,0,0,0,0]];
this.hard = [[0,0,0,1,0,0,0,0,0],[0,1,6,9,0,0,8,0,0],[0,0,0,0,8,0,6,0,0],[0,0,0,5,0,0,0,0,0],[0,0,0,0,6,3,0,0,0],[0,0,0,0,0,0,0,0,0],[0,7,5,0,0,0,0,0,0],[9,0,3,0,0,0,0,0,5],[8,0,0,0,5,0,3,1,0]];
}
public getCorrection(): number[][]{
@ -46,17 +48,17 @@ export class SudokuService {
}
public validationGrille(userGrille: number[][]): number[][] {
// valide la grille et envoit la position des erreurs
let validation: number[][] = [];
for (let x:number = 0; x<userGrille.length; x++) {
for (let y:number = 0; y<userGrille.length; y++) {
validation.push([]);
for (let y:number = 0; y<userGrille[x].length; y++) {
if (userGrille[x][y] == 0) {
validation[x].push(0);
}
if (userGrille[x][y] == this.correction[x][y]) {
validation[x].push(1);
for (let x:number = 0; x<userGrille[y].length; x++) {
if (userGrille[y][x] == 0) {
validation[y].push(0);
} else if (userGrille[y][x] == this.correction[y][x]) {
validation[y].push(1);
} else {
validation[x].push(-1);
validation[y].push(-1);
}
}
}
@ -64,12 +66,45 @@ export class SudokuService {
}
public isCorrect(userGrille: number[][]): boolean {
// verrifie si la grille est complette et juste
for (let x:number = 0; x<userGrille.length; x++) {
for (let y:number = 0; y<userGrille[x].length; y++) {
if (userGrille[x][y] == 0 || userGrille[x][y] != this.correction[x][y]) return true;
if (userGrille[x][y] == 0 || userGrille[x][y] != this.correction[x][y]) return false;
}
}
return true;
}
public publishGame(userGrille: number[][],pseudo: string, nbIndice: number): boolean {
if (!this.isCorrect(userGrille)) return false;
this.http.post<string>(this.saveGameApiUrl,{"date":new Date(),"playerName":pseudo,"clues":nbIndice}).subscribe();
return true;
}
private transformFormat(grill: number[][]): number[][] {
// convertie le format de la grille pour faciliter l'affichage après.
let newgrid: number[][] = [];
for (let y=0; y<9; y++) {
newgrid.push([]);
for (let x=0; x<9; x++) {
newgrid[y].push(0);
}
}
for (let y=0; y<grill.length; y++) {
for (let x=0; x<grill[y].length; x++) {
let a = this.getXYfromOtherXY(x,y);
newgrid[a[1]][a[0]] =grill[y][x];
}
}
return newgrid;
}
private getXYfromOtherXY(x:number, y:number):number[] {
// transforme les coordonées d'un point du systhème de représentation de l'API au systhème de représentation interne à l'application
let a = Math.floor(x/3);
let b = Math.floor(y/3);
let newy:number = b*3+a;
let newx:number = (y%3)*3+(x%3);
return [newx,newy];
}
}

@ -6,7 +6,7 @@ import { User } from "../models/user.model";
export class UserService {
private users: User[];
private readonly userApiUrl = 'https://664ba07f35bbda10987d9f99.mockapi.io/api/users';
private pageSize: number = 5;
public readonly pageSize: number = 5;
public constructor(private http: HttpClient){
this.users = [];
@ -34,13 +34,25 @@ export class UserService {
return Math.ceil(this.users.length / this.pageSize)
}
public getIdByUser(name: String): number | undefined{
let u : User | undefined = this.users.find((user) => user.login === name)
if(u == undefined) { return }
return u.id
}
public getStreaksFromId(id: number): Object[] | undefined{
let u : User | undefined = this.users.find((user) => user.id === id)
if(u == undefined) { return }
return u.streaks
}
public getUserNbPts(nbPage:number, sortSens:number): User[] {
if (nbPage>this.getNbPage() || nbPage<=0) return[];
let page = this.HardCopy(this.users)
let page = this.HardCopy(this.users);
page = page.sort((a,b) => {
if (a.points == null || b.points == null) return 0;
if (a.points > b.points) return 1 * sortSens;
if (b.points > a.points) return -1 * sortSens;
if (a.points>b.points) return 1*sortSens;
if (b.points>a.points) return -1*sortSens;
return 0;
});
page = this.getSubset(page,(nbPage-1)*this.pageSize,nbPage*this.pageSize);
@ -68,18 +80,6 @@ export class UserService {
return JSON.parse(JSON.stringify(users));
}
public getIdByUser(name: String): number | undefined{
let u : User | undefined = this.users.find((user) => user.login === name)
if(u == undefined) { return }
return u.id
}
public getStreaksFromId(id: number): Object[] | undefined{
let u : User | undefined = this.users.find((user) => user.id === id)
if(u == undefined) { return }
return u.streaks
}
private getSubset(user: User[],deb:number,fin:number):User[] {
let a: User[] = [];
for (let i=0; i<user.length; i++) {
@ -89,4 +89,13 @@ export class UserService {
}
return a;
}
public addToLocalStorage(user : User){
localStorage.setItem('UserConnecte',String(user.login))
}
public getItemUserConnecte() : String | null{
return localStorage.getItem('UserConnecte')
}
}

@ -3,6 +3,8 @@
<head>
<meta charset="utf-8">
<title>AngularTp1</title>
<!-- <base href="/containers/ludoviccastiglia-sudoku/"> -->
<!-- pour executer en local commentez la ligne du dessus et décommentez la ligne en dessous -->
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">

@ -28,5 +28,5 @@
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
},
}

Loading…
Cancel
Save