rajah преди 10 месеца
родител
ревизия
ab6698a39b

+ 2 - 0
src/app/app.routes.ts

@@ -34,6 +34,7 @@ import { ProductionUpdateComponent } from './composants/production-update/produc
 import { ProductionUploadComponent } from './composants/production-upload/production-upload.component';
 import { ShowListComponent } from './composants/show-list/show-list.component';
 import { ShowLinksComponent } from './composants/show-links/show-links.component';
+import { ShowUploadComponent } from './composants/show-upload/show-upload.component';
 
 export const routes: Routes = [
   {path: '', redirectTo: 'home', pathMatch: 'full'},
@@ -64,6 +65,7 @@ export const routes: Routes = [
   {path: 'production-upload/:numeroProduction', component: ProductionUploadComponent, canActivate: [UserGuard], runGuardsAndResolvers: 'always' },
   {path: 'show-list', component: ShowListComponent, canActivate: [AdminGuard], runGuardsAndResolvers: 'always' },
   {path: 'show-links/:numeroCategorie', component: ShowLinksComponent, canActivate: [AdminGuard], runGuardsAndResolvers: 'always' },
+  {path: 'show-upload/:numeroProduction', component: ShowUploadComponent, canActivate: [AdminGuard], runGuardsAndResolvers: 'always' },
 ];
 
 @NgModule({

+ 1 - 1
src/app/composants/production-details/production-details.component.html

@@ -77,7 +77,7 @@
 		<div class="form-group row">
 			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Gestionnaire</span></label>
 			<div class="col-sm-8">
-				<input type="text" class="form-control form-control-sm field-separate" id="plateforme" name="plateforme" [(ngModel)]="production.nomGestionnaire" disabled>
+				<input type="text" class="form-control form-control-sm field-separate" id="gestionnaire" name="gestionnaire" [(ngModel)]="production.nomGestionnaire" disabled>
 			</div>
 		</div>
 

+ 2 - 1
src/app/composants/production-details/production-details.component.ts

@@ -1,9 +1,10 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
+import { FormsModule } from '@angular/forms';
+
 import { MenuComponent } from '../menu/menu.component';
 import { ProductionShort, ProductionEnum, ProductionTypeList } from '../../interfaces/production';
 import { ProductionService } from '../../services/production.service';
-import { FormsModule } from '@angular/forms';
 
 @Component({ selector: 'app-production-details', imports: [FormsModule, MenuComponent], templateUrl: './production-details.component.html', styleUrl: './production-details.component.css' })
 

+ 2 - 1
src/app/composants/production-upload/production-upload.component.ts

@@ -1,9 +1,10 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
+import { FormsModule } from '@angular/forms';
+
 import { MenuComponent } from '../menu/menu.component';
 import { ProductionFile } from '../../interfaces/production';
 import { ProductionService } from '../../services/production.service';
-import { FormsModule } from '@angular/forms';
 
 @Component({ selector: 'app-production-upload', imports: [FormsModule, MenuComponent], templateUrl: './production-upload.component.html', styleUrl: './production-upload.component.css' })
 

+ 3 - 0
src/app/composants/show-list/show-list.component.css

@@ -0,0 +1,3 @@
+.show_row { height:30rem;overflow-x:auto; }
+.show_item { min-width:10rem;max-width:17rem; }
+.show_red { border-color: #DD0000; }

+ 7 - 4
src/app/composants/show-list/show-list.component.html

@@ -22,12 +22,12 @@
     @if (categorie.computed) { }
     @if (categorie.displayable) { }
   </div>
-  <div class="card-body hstack align-items-baseline" style="height:30rem;overflow-x:auto;">
+  <div class="card-body hstack align-items-baseline show_row">
 {{ resetLettre() }} @for (production of productions; track production.numeroProduction) { @if (categorie.numeroCategorie == production.numeroCategorie) {
-    <div class="card shadow" style="min-width:10rem;max-width:17rem;">
-      <img src="{{ production.vignette }}" class="img-fluid card-img-top pointeur-souris" (click)="formProduction(production.numeroProduction)">
+    <div class="card shadow show_item" [class.show_red]="production.etatMedia === 0">
+      <img src="{{ production.vignette }}" class="img-fluid card-img-top pointeur-souris" (click)="formPresentation(production.numeroProduction)">
       <div class="card-header">
-        <span class="card-title">{{ nextLettre() }} -<a (click)="formProduction(production.numeroProduction)" class="text-primary pointeur-souris" style="margin-left:7px;">{{ production.titre }}</a></span><br/>
+        <span class="card-title">{{ nextLettre() }} -<a (click)="formPresentation(production.numeroProduction)" class="text-primary pointeur-souris" style="margin-left:7px;">{{ production.titre }}</a></span><br/>
         <small><span class="text-muted">par</span> {{ production.auteurs }} @if (production.groupes) { @if (production.groupes.length > 0) { &nbsp;/&nbsp;{{ production.groupes }} } }</small>
       </div>
       <div class="card-body">
@@ -38,6 +38,9 @@
       <div class="card-footer d-flex justify-content-between">
         <i class="fa-solid fa-download text-primary pointeur-souris" (click)="getFile(production.numeroProduction, production.nomArchive)" tooltip="{{ production.nomArchive }} (v{{ production.numeroVersion }})" placement="top" container="body"></i>
         <i class="fa-solid fa-user-tie text-muted" style="margin-left:7px;" i18n-tootip tooltip="géré par {{ production.nomGestionnaire }}" placement="top" container="body"></i>
+        @if (production.etatMedia === 0) { <i class="fa-solid fa-circle-xmark text-danger ms-auto" style="margin-left:7px;" (click)="formPresentation(production.numeroProduction)" i18n-tootip tooltip="nécessite le média pour présentation" placement="top" container="body"></i> }
+        @else if (production.etatMedia === 1) { <i class="fa-solid fa-circle-check text-success ms-auto" style="margin-left:7px;" (click)="formPresentation(production.numeroProduction)" i18n-tootip tooltip="média présent" placement="top" container="body"></i> }
+        @else if (production.etatMedia === 2) { <i class="fa-solid fa-square-arrow-up-right text-success ms-auto" style="margin-left:7px;" (click)="formPresentation(production.numeroProduction)" i18n-tootip tooltip="média hors fichier de présentation" placement="top" container="body"></i> }
       </div>
     </div>
 } }

+ 1 - 1
src/app/composants/show-list/show-list.component.ts

@@ -47,7 +47,7 @@ export class ShowListComponent implements OnInit
 
   getVersionPDF() { this.presentationService.getPresentationPDF().subscribe(response => { this.savePDF(response.body, 'presentations.pdf'); }); }
 
-  formProduction(id: number) { this.router.navigate(['/production-details', id]); }
+  formPresentation(id: number) { this.router.navigate(['/show-upload', id]); }
 
   lierProductions(id: number) { this.router.navigate(['/show-links', id]); }
 

+ 0 - 0
src/app/composants/show-upload/show-upload.component.css


+ 105 - 0
src/app/composants/show-upload/show-upload.component.html

@@ -0,0 +1,105 @@
+<app-menu></app-menu>
+<div id="main">
+<form #formRef="ngForm" (ngSubmit)="addPresentationFile()" class="needs-validation">
+
+  <div class="card shadow center">
+    <div class="card-header"><span i18n>Choix du média pour la présentation d'une production</span></div>
+  	<div class="card-header shadow-sm">
+  		<button (click)="goToListPresentation()" class="btn bg-gradient btn-primary btn-sm"><i class="fa-solid fa-xmark"></i>&nbsp;<span i18n>Retour</span></button>
+  	</div>
+  	<div class="card-body scrollable">
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Type</span></label>
+  			<div class="col-sm-8">
+  				<div class="form-group field-separate">
+  					<select class="form-select form-select-sm" id="type" name="type" [(ngModel)]="production.type" disabled>
+  					  @for (type of types; track type.key) { <option [value]="type.key">{{ type.value }}</option> }
+  					</select>
+  				</div>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Titre</span></label>
+  			<div class="col-sm-8">
+  				<input type="text" class="form-control form-control-sm field-separate" id="titre" name="titre" [(ngModel)]="production.titre" disabled>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Auteur(s)</span></label>
+  			<div class="col-sm-8">
+  				<input type="text" class="form-control form-control-sm field-separate" id="auteurs" name="auteurs" [(ngModel)]="production.auteurs" disabled>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Groupe(s)</span></label>
+  			<div class="col-sm-8">
+  				<input type="text" class="form-control form-control-sm field-separate" id="groupes" name="groupes" [(ngModel)]="production.groupes" disabled>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Plateforme</span></label>
+  			<div class="col-sm-8">
+  				<input type="text" class="form-control form-control-sm field-separate" id="plateforme" name="plateforme" [(ngModel)]="production.plateforme" disabled>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Commentaire</span></label>
+  			<div class="col-sm-8">
+  				<textarea class="form-control form-control-sm field-separate" id="commentaire" name="commentaire" [(ngModel)]="production.commentaire" disabled></textarea>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Informations privées</span></label>
+  			<div class="col-sm-8">
+  				<textarea class="form-control form-control-sm field-separate" id="informationsPrivees" name="informationsPrivees" [(ngModel)]="production.informationsPrivees" disabled></textarea>
+  			</div>
+  		</div>
+
+  		<div class="form-group row">
+  			<label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Gestionnaire</span></label>
+  			<div class="col-sm-8">
+  				<input type="text" class="form-control form-control-sm field-separate" id="gestionnaire" name="gestionnaire" [(ngModel)]="production.nomGestionnaire" disabled>
+  			</div>
+  		</div>
+
+      <div class="form-group row">
+        <label class="col-sm-4 col-form-label col-form-label-sm label-nobr"></label>
+  			<div class="col-sm-8">
+          <div class="form-check form-control-sm field-separate">
+            <input class="form-check-input" type="radio" name="radioEtatMedia" id="radioEtatMedia0">
+            <label class="form-check-label" for="radioEtatMedia0"><span i18n class="text-danger">Aucun média à présenter</span></label>
+          </div>
+          <div class="form-check form-control-sm field-separate">
+            <input class="form-check-input" type="radio" name="radioEtatMedia" id="radioEtatMedia2">
+            <label class="form-check-label" for="radioEtatMedia2"><span i18n>Le média est externe (exécutable à lancer sur machine spécifique, fichier trop gros, etc)</span></label>
+          </div>
+          <div class="form-check form-control-sm field-separate">
+            <input class="form-check-input" type="radio" name="radioEtatMedia" id="radioEtatMedia1">
+            <label class="form-check-label" for="radioEtatMedia1"><span i18n>Le média est interne à la présentation, à téléverser</span></label>
+          </div>
+        </div>
+      </div>
+
+			<div class="form-group row">
+        <label class="col-sm-4 col-form-label col-form-label-sm label-nobr"><span i18n>Média</span></label>
+				<div class="col-sm-8">
+          <input type="file" class="form-control form-control-sm field-separate" id="media" name="media" accept="audio/*,video/*,image/*" (change)="onMediaSelected($event)">
+          <div class="form-text field-separate"><span i18n>La taille du média est limitée à 2 Go.</span></div>
+				</div>
+			</div>
+
+  	</div>
+  	<div class="card-footer">
+      <button type="button" class="btn bg-gradient btn-success btn-sm text-left" type="submit" [disabled]="formRef.invalid"><i class="fa-solid fa-plus"></i>&nbsp;<span i18n>Valider</span></button>
+  	</div>
+  </div>
+
+</form>
+</div>

+ 56 - 0
src/app/composants/show-upload/show-upload.component.ts

@@ -0,0 +1,56 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { FormsModule } from '@angular/forms';
+
+import { MenuComponent } from '../menu/menu.component';
+import { PresentationFile } from '../../interfaces/production';
+import { PresentationService } from '../../services/presentation.service';
+
+import { ProductionShort, ProductionEnum, ProductionTypeList } from '../../interfaces/production';
+import { ProductionService } from '../../services/production.service';
+
+@Component({ selector: 'app-show-upload', imports: [FormsModule, MenuComponent], templateUrl: './show-upload.component.html', styleUrl: './show-upload.component.css' })
+
+export class ShowUploadComponent implements OnInit
+{
+
+  numeroProduction: number = 0;
+
+  types: ProductionEnum[] = ProductionTypeList;
+
+  production: ProductionShort = new ProductionShort();
+
+  media: PresentationFile = new PresentationFile();
+
+  constructor(private productionService: ProductionService, private presentationService: PresentationService, private route: ActivatedRoute, private router: Router, private menu: MenuComponent) { }
+
+  ngOnInit()
+  {
+    this.numeroProduction = this.route.snapshot.params['numeroProduction'];
+    this.production = new ProductionShort();
+    this.productionService.getByIdProduction(this.numeroProduction).subscribe( data => { this.production = data; });
+  }
+
+
+  onMediaSelected(event: any)
+  {
+    const et = event.target;
+    const reader = new FileReader();
+
+    if (et.files && et.files.length > 0)
+    {
+      const file = et.files[0];
+
+      reader.onloadend = async (e: any) => { if (e.target.result) { this.media.mediaData = e.target.result; } }
+
+      reader.readAsDataURL(file);
+		}
+  }
+
+  private saveMedia() { this.presentationService.uploadMediaFile(this.numeroProduction, this.media).subscribe(); this.goToListPresentation(); }
+
+  addPresentationFile() { this.saveMedia(); }
+
+  goToListPresentation() {this.router.navigate(['/show-list'], { queryParams: { 'refresh': this.menu.getRandomInteger(1, 100000) } }); }
+
+}

+ 9 - 0
src/app/interfaces/production.ts

@@ -48,6 +48,7 @@ export class ProductionShort
   numeroVersion: number = 0;
   numeroCategorie: number = 0;
   ordrePresentation: number = 0;
+  etatMedia: number = 0;
 }
 
 export class ProductionFile
@@ -69,3 +70,11 @@ export class ProductionItem
   plateforme: string = "";
   numeroOrdre: number = 0;
 }
+
+export class PresentationFile
+{
+  numeroProduction: number = 0;
+  etatMedia: number = 0;
+  mediaMime: string = "application/octet-stream";
+  mediaData!: string | any;
+}

+ 3 - 1
src/app/services/presentation.service.ts

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
 import { HttpClient, HttpParams, HttpHeaders, HttpResponse } from '@angular/common/http'
 import { Observable } from 'rxjs';
 import { Environnement } from '../env';
-import { ProductionShort, ProductionItem } from '../interfaces/production';
+import { ProductionShort, ProductionItem, PresentationFile } from '../interfaces/production';
 
 @Injectable({ providedIn: 'root' })
 
@@ -71,4 +71,6 @@ export class PresentationService
     return this.httpClient.get(`${this.baseURL}/down`, { params: params });
   }
 
+  uploadMediaFile(id: number, media: PresentationFile): Observable<Object>{ return this.httpClient.put(`${this.baseURL}/upload/${id}`, media); }
+
 }