Compare commits

...

25 Commits

Author SHA1 Message Date
VANNEAU a33f1ab1e2 Correction docker 11 months ago
floxx2112 3d3b9a7a45 DockerFIle 11 months ago
floxx2112 10736d718a opti major + refact tailwind for btn + filter 12 months ago
floxx2112 0654581c2b opti + review on select tag 1 year ago
floxx2112 99791e3b2e multiples changes and opti (git ticket) 1 year ago
floxx2112 cc47e34572 new pipe filter table + rm display all tables on search page 1 year ago
floxx2112 dc25dc6da9 Opti structure - remove any 1 year ago
floxx2112 be8f6fee51 add little tooltip 1 year ago
floxx2112 45c5f85a62 fix responsive 1 year ago
floxx2112 5d2a682744 little adjustement and opti 1 year ago
floxx2112 2f4718886b opti + end of mulitples functionnalities 1 year ago
floxx2112 c4de57ab5c update tag and add link Tag/script 1 year ago
floxx2112 fbee9b6857 add script and tags management 1 year ago
floxx2112 1eb347b5f8 main update (script management) 1 year ago
floxx2112 9571e2f4ff add loader + dialog box + maj port 1 year ago
fhibert be40370cb2 Mise à jour de 'src/app/navbar/navbar.component.html' 1 year ago
fhibert d2939b5b57 Mise à jour de 'src/app/navbar/navbar.component.html' 1 year ago
fhibert 60f4b7cce6 Mise à jour de 'src/app/navbar/navbar.component.html' 1 year ago
floxx2112 9952610b88 Add search by table and schema + checkbox for display views or not 1 year ago
floxx2112 2646b80461 add navigate and good server port 1 year ago
floxx2112 b78d4dc5a4 add button for the refresh db function 1 year ago
floxx2112 3a8c52ae76 remove useless lines 1 year ago
floxx2112 b4ae128b86 add multiples choices for columns on joins + finalize function 1 year ago
floxx2112 83ec591545 Joins functionnalities complete v1 1 year ago
floxx2112 489e3fa34c Init App 1 year ago
  1. 16
      .editorconfig
  2. 42
      .gitignore
  3. 4
      .vscode/extensions.json
  4. 20
      .vscode/launch.json
  5. 42
      .vscode/tasks.json
  6. 27
      Dockerfile
  7. 27
      README.md
  8. 139
      angular.json
  9. 22
      nginx.conf
  10. 12301
      package-lock.json
  11. 46
      package.json
  12. 7
      proxy.conf.json
  13. 32
      src/app/app-routing.module.ts
  14. 6
      src/app/app.component.html
  15. 0
      src/app/app.component.scss
  16. 10
      src/app/app.component.ts
  17. 79
      src/app/app.module.ts
  18. 19
      src/app/creation/columns-card/columns-card.component.html
  19. 0
      src/app/creation/columns-card/columns-card.component.scss
  20. 37
      src/app/creation/columns-card/columns-card.component.ts
  21. 74
      src/app/creation/create-joins-page/create-joins-page.component.html
  22. 0
      src/app/creation/create-joins-page/create-joins-page.component.scss
  23. 452
      src/app/creation/create-joins-page/create-joins-page.component.ts
  24. 55
      src/app/creation/joins-table/joins-table.component.html
  25. 25
      src/app/creation/joins-table/joins-table.component.scss
  26. 198
      src/app/creation/joins-table/joins-table.component.ts
  27. 41
      src/app/creation/where-card/where-card.component.html
  28. 0
      src/app/creation/where-card/where-card.component.scss
  29. 54
      src/app/creation/where-card/where-card.component.ts
  30. 20
      src/app/custom-pipe/CustomFilterScript.ts
  31. 36
      src/app/custom-pipe/CustomInfoTableSearchPipe.ts
  32. 62
      src/app/custom-pipe/Custom_Ng2SearchPipe.ts
  33. 42
      src/app/directives/dnd.directive.ts
  34. 141
      src/app/joins/joins-page/joins-page.component.html
  35. 0
      src/app/joins/joins-page/joins-page.component.scss
  36. 130
      src/app/joins/joins-page/joins-page.component.ts
  37. 24
      src/app/landing-page/landing-page.component.html
  38. 11
      src/app/landing-page/landing-page.component.scss
  39. 10
      src/app/landing-page/landing-page.component.ts
  40. 72
      src/app/manager/add-script/add-script.component.html
  41. 0
      src/app/manager/add-script/add-script.component.scss
  42. 183
      src/app/manager/add-script/add-script.component.ts
  43. 40
      src/app/manager/edit-script/edit-script.component.html
  44. 0
      src/app/manager/edit-script/edit-script.component.scss
  45. 109
      src/app/manager/edit-script/edit-script.component.ts
  46. 112
      src/app/manager/script-management/script-management.component.html
  47. 0
      src/app/manager/script-management/script-management.component.scss
  48. 192
      src/app/manager/script-management/script-management.component.ts
  49. 58
      src/app/manager/tags-management/tags-management.component.html
  50. 0
      src/app/manager/tags-management/tags-management.component.scss
  51. 115
      src/app/manager/tags-management/tags-management.component.ts
  52. 60
      src/app/menu/navbar/navbar.component.html
  53. 4
      src/app/menu/navbar/navbar.component.scss
  54. 52
      src/app/menu/navbar/navbar.component.ts
  55. 5
      src/app/model/alias.ts
  56. 5
      src/app/model/column-data.ts
  57. 7
      src/app/model/condition.ts
  58. 7
      src/app/model/employee.ts
  59. 7
      src/app/model/info-column.ts
  60. 6
      src/app/model/info-table.ts
  61. 9
      src/app/model/join.ts
  62. 6
      src/app/model/link-script-tag.ts
  63. 5
      src/app/model/script.ts
  64. 5
      src/app/model/tag.ts
  65. 96
      src/app/search/request-page/request-page.component.html
  66. 0
      src/app/search/request-page/request-page.component.scss
  67. 171
      src/app/search/request-page/request-page.component.ts
  68. 35
      src/app/service/info-column.service.ts
  69. 41
      src/app/service/info-table.service.ts
  70. 19
      src/app/service/reset_data.service.ts
  71. 44
      src/app/service/script.service.ts
  72. 36
      src/app/service/tags.service.ts
  73. 0
      src/assets/.gitkeep
  74. 51
      src/assets/button.scss
  75. BIN
      src/assets/images/apsidetop.png
  76. BIN
      src/assets/images/card-top.jpg
  77. BIN
      src/assets/images/logo-apsidetop-blanc.png
  78. BIN
      src/assets/images/snapface.png
  79. BIN
      src/assets/images/userpng.png
  80. 1
      src/assets/svg/arrows.svg
  81. 1
      src/assets/svg/copy.svg
  82. 1
      src/assets/svg/edit.svg
  83. 1
      src/assets/svg/trash.svg
  84. 1
      src/assets/svg/upload.svg
  85. 3
      src/environments/environment.development.ts
  86. 3
      src/environments/environment.ts
  87. BIN
      src/favicon.ico
  88. 16
      src/index.html
  89. 7
      src/main.ts
  90. 13
      src/styles.scss
  91. 17
      tailwind.config.js
  92. 14
      tsconfig.app.json
  93. 33
      tsconfig.json
  94. 14
      tsconfig.spec.json

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

42
.gitignore vendored

@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "pwa-chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored

@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

@ -0,0 +1,27 @@
## STEP 1 BUILD ##
FROM node:18-alpine AS build
RUN mkdir -p /app
WORKDIR /app
RUN npm cache clean --force
COPY package*.json /app
RUN npm install
COPY . /app
RUN npm run build --prod
## STEP 2 DEPLOY ##
FROM nginx:1.21.3 AS ngi
COPY --from=build /app/dist/* /usr/share/nginx/html
COPY /nginx.conf /etc/nginx/conf.d/default.conf
# COPY /nginx.conf /etc/nginx/nginx.conf
# EXPOSE 9002
CMD ["nginx", "-g", "daemon off;"]

@ -0,0 +1,27 @@
# AngularTest
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.1.4.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

@ -0,0 +1,139 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angularTest": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss",
"skipTests": true
},
"@schematics/angular:class": {
"skipTests": true
},
"@schematics/angular:directive": {
"skipTests": true
},
"@schematics/angular:guard": {
"skipTests": true
},
"@schematics/angular:interceptor": {
"skipTests": true
},
"@schematics/angular:pipe": {
"skipTests": true
},
"@schematics/angular:resolver": {
"skipTests": true
},
"@schematics/angular:service": {
"skipTests": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/angular-test",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "angularTest:build:production"
},
"development": {
"browserTarget": "angularTest:build:development"
}
},
"defaultConfiguration": "development",
"options": {
"port": 9002
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angularTest:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
}
}
}
}
}
}

@ -0,0 +1,22 @@
upstream docker-web {
server 178.18.0.5:9001;
}
server {
include /etc/nginx/extra-conf.d/*.conf;
listen 80;
include /etc/nginx/mime.types;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://docker-web;
}
}

12301
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,46 @@
{
"name": "angular-test",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.1.0",
"@angular/cdk": "^15.1.4",
"@angular/common": "^15.1.0",
"@angular/compiler": "^15.1.0",
"@angular/core": "^15.1.0",
"@angular/forms": "^15.1.0",
"@angular/material": "^15.1.4",
"@angular/platform-browser": "^15.1.0",
"@angular/platform-browser-dynamic": "^15.1.0",
"@angular/router": "^15.1.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.9",
"ng-multiselect-dropdown": "^0.3.9",
"ng2-search-filter": "^0.5.1",
"ngx-dropzone": "^3.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.1.4",
"@angular/cli": "~15.1.4",
"@angular/compiler-cli": "^15.1.0",
"@types/jasmine": "~4.3.0",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"tailwindcss": "^3.2.4",
"typescript": "~4.9.4"
}
}

@ -0,0 +1,7 @@
{
"/api/*": {
"target": "http://localhost:9002",
"secure": false,
"timeout": 600000
}
}

@ -0,0 +1,32 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LandingPageComponent } from './landing-page/landing-page.component';
import { RequestPageComponent } from './search/request-page/request-page.component';
import { JoinsPageComponent } from './joins/joins-page/joins-page.component';
import { CreateJoinsPageComponent } from './creation/create-joins-page/create-joins-page.component';
import { ScriptManagementComponent } from './manager/script-management/script-management.component';
import { AddScriptComponent } from './manager/add-script/add-script.component';
import { EditScriptComponent } from './manager/edit-script/edit-script.component';
import { TagsManagementComponent } from './manager/tags-management/tags-management.component';
const routes: Routes = [
{ path: '', component: LandingPageComponent },
{ path: 'request', component: RequestPageComponent },
{ path: 'joins', component: JoinsPageComponent },
{ path: 'createjoins', component: CreateJoinsPageComponent },
{ path: 'scriptmanagement', component: ScriptManagementComponent },
{ path: 'addscript', component: AddScriptComponent},
{ path: 'editscript', component: EditScriptComponent},
{ path: 'tagsmanagement', component: TagsManagementComponent},
{ path: '**', redirectTo: '', pathMatch: 'full'}
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}

@ -0,0 +1,6 @@
<div>
<app-navbar></app-navbar>
<div class="mt-4 px-12">
<router-outlet></router-outlet>
</div>
</div>

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
}

@ -0,0 +1,79 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { LandingPageComponent } from './landing-page/landing-page.component';
import { NavbarComponent } from './menu/navbar/navbar.component';
import { RequestPageComponent } from './search/request-page/request-page.component';
import { HttpClientModule } from '@angular/common/http';
import { EmployeeService } from './service/employee.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatStepperModule } from '@angular/material/stepper';
import { MatToolbarModule } from '@angular/material/toolbar';
import {MatDialogModule} from '@angular/material/dialog';
import { MatIconModule } from "@angular/material/icon";
import { Custom_Ng2SearchPipe } from './custom-pipe/Custom_Ng2SearchPipe';
import { FormsModule } from '@angular/forms';
import { JoinsPageComponent } from './joins/joins-page/joins-page.component';
import { CreateJoinsPageComponent } from './creation/create-joins-page/create-joins-page.component';
import { JoinsTableComponent } from './creation/joins-table/joins-table.component';
import { WhereCardComponent } from './creation/where-card/where-card.component';
import { ColumnsCardComponent } from './creation/columns-card/columns-card.component';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import { ScriptManagementComponent } from './manager/script-management/script-management.component';
import { AddScriptComponent } from './manager/add-script/add-script.component';
import { DndDirective } from './directives/dnd.directive'
import { CustomFilterScript } from 'src/app/custom-pipe/CustomFilterScript';
import { CustomFilterTable } from 'src/app/custom-pipe/CustomInfoTableSearchPipe';
import { EditScriptComponent } from './manager/edit-script/edit-script.component';
import { TagsManagementComponent } from './manager/tags-management/tags-management.component';
import { NgMultiSelectDropDownModule } from 'ng-multiselect-dropdown';
import { CdkAccordionModule } from '@angular/cdk/accordion';
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard';
import {MatTooltipModule} from '@angular/material/tooltip';
@NgModule({
declarations: [
AppComponent,
LandingPageComponent,
NavbarComponent,
RequestPageComponent,
Custom_Ng2SearchPipe,
CustomFilterScript,
CustomFilterTable,
JoinsPageComponent,
CreateJoinsPageComponent,
JoinsTableComponent,
WhereCardComponent,
ColumnsCardComponent,
ScriptManagementComponent,
AddScriptComponent,
DndDirective,
EditScriptComponent,
TagsManagementComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
BrowserAnimationsModule,
MatStepperModule,
MatToolbarModule,
MatIconModule,
FormsModule,
MatDialogModule,
MatProgressSpinnerModule,
NgMultiSelectDropDownModule,
CdkAccordionModule,
ClipboardModule,
MatTooltipModule
],
providers: [EmployeeService],
bootstrap: [AppComponent]
})
export class AppModule { }

@ -0,0 +1,19 @@
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 rounded-lg overflow-hidden shadow-lg bg-slate-300 border-solid border-orange-400 border-2 p-2">
<div>
<label class="text-black text-left">Colonnes(Base) : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterBaseColumn" placeholder="Rechercher...">
<select [(ngModel)]="baseColumnData" (change)="sendColumnsData()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let col of baseColumns | filter : filterBaseColumn" id="{{ col.id }}" [ngValue]="col">{{ col.nameColumn }} ({{col.columnText}})</option>
</select>
</div>
<div>
<label class="text-black text-left">Colonnes : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterSpecificColumn" placeholder="Rechercher...">
<select [(ngModel)]="specificColumnData" (change)="sendColumnsData()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let col of specificColumns | filter : filterSpecificColumn" id="{{ col.id }}" [ngValue]="col">{{ col.nameColumn }} ({{col.columnText}})</option>
</select>
</div>
</div>

@ -0,0 +1,37 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { InfoColumn } from '../../model/info-column';
import { ColumnData } from '../../model/column-data';
@Component({
selector: 'app-columns-card',
templateUrl: './columns-card.component.html',
styleUrls: ['./columns-card.component.scss']
})
export class ColumnsCardComponent {
@Input() baseColumns: InfoColumn[] | undefined
@Input() specificColumns: InfoColumn[] | undefined
@Input() childIndex: number | undefined;
@Output() selectedColumn = new EventEmitter<ColumnData>();
specificColumnData: InfoColumn | undefined;
baseColumnData: InfoColumn | undefined;
filterBaseColumn: string = "";
filterSpecificColumn: string = "";
constructor(){}
public sendColumnsData(){
if(this.baseColumnData && this.specificColumnData){
let tempsOpe = "AND";
if(this.childIndex === 0){
tempsOpe = "ON";
}
const tempColumnData: ColumnData = {
operator: tempsOpe,
aliasColumn: this.baseColumnData.nameColumn,
specColumn: this.specificColumnData.nameColumn,
}
this.selectedColumn.emit(tempColumnData);
}
}
}

@ -0,0 +1,74 @@
<!--Card-->
<div class="flex sm:flex-nowrap flex-wrap sm:space-x-4 my-4">
<div class="w-full sm:w-2/3 h-full text-center rounded-lg overflow-hidden shadow-lg bg-white p-4 ">
<div class="rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2 p-4">
<div class="font-bold text-xl text-left">Selectionner un Schéma et une Table (Base) :</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="mt-2">
<label class="text-black text-left">Schémas : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterBaseSchema" placeholder="Rechercher...">
<select (change)="useTablesWithBaseSchema($event)" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of allSchema | filter : filterBaseSchema" value="{{ schema }}">{{ schema }}</option>
</select>
</div>
<div *ngIf="tablesByBaseSchema" class="mt-2">
<label *ngIf="!isChecked" class="text-black text-left">Tables : </label>
<label *ngIf="isChecked" class="text-black text-left">Tables et Vues : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterBaseTables" placeholder="Rechercher...">
<select [(ngModel)]="baseTableData" (change)="getNameBaseTable()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of tablesByBaseSchema | filter : filterBaseTables" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
</div>
</div>
<div *ngIf="tablesByBaseSchema">
<input type="checkbox" [(ngModel)]="isChecked" (change)="checkValue(isChecked?'true':'false')" class="w-4 h-4 text-orange-400 bg-slate-400 border-slate-200 rounded focus:ring-orange-400 focus:ring-2"/>
<label class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Afficher aussi les Vues</label>
</div>
</div>
<div *ngIf="baseTableName">
<div *ngFor="let hop of requestInfoJoin; index as i" >
<app-joins-table [allAlias]="aliasData" [passJoinData]="allData[i]" (data)="retrieveData($event, i)" id="{{hop}}"></app-joins-table>
<div class="sm:inline-flex sm:space-x-4">
<button (click)="addTable()" type="button" class="btn-grad-pink-orange mr-2 mb-2">Ajouter une Jointure</button>
<button *ngIf="!oneJoinRemaining" (click)="removeTable(i)" type="button" class="btn-grad-red-yellow mr-2 mb-2">Supprimer cette jointure</button>
<button (click)="addCondition()" type="button" class="btn-grad-green-blue mr-2 mb-2">Ajouter une Condition</button>
</div>
</div>
</div>
<div *ngIf="whereCardList.length != 0">
<div *ngFor="let whereCards of whereCardList; index as i" >
<app-where-card [allAlias]="aliasData" [childIndex]="i" id="{{whereCards}}" (conditionData)="getConditionData($event, i)"></app-where-card>
<div class="sm:inline-flex sm:space-x-4">
<button (click)="addCondition()" type="button" class="btn-grad-green-blue mr-2 mb-2">Ajouter une Condition</button>
<button (click)="removeWhereCard(i)" type="button" class="btn-grad-cyon-blue mr-2 mb-2">Supprimer cette Condition</button>
</div>
</div>
</div>
</div>
<div class="sticky top-20 w-full mt-2 sm:mt-0 sm:w-1/3 h-full text-center rounded-lg overflow-hidden shadow-lg bg-white p-4 ">
<div class="flex justify-between mb-2">
<div class="text-xl font-bold">Résultat de la requête :</div>
<mat-icon matTooltip="Copier la requête" [cdkCopyToClipboard]="newRequestData" class="text-green-400 ml-2 hover:cursor-pointer" svgIcon="copy"></mat-icon>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-slate-900 text-white text-left pl-2">{{newRequestData}}
</div>
</div>
</div>

@ -0,0 +1,452 @@
import { HttpErrorResponse } from '@angular/common/http';
import { AfterViewInit, Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { InfoTable } from '../../model/info-table';
import { InfoColumnService } from '../../service/info-column.service';
import { InfoTableService } from '../../service/info-table.service';
import { JoinsTableComponent } from '../joins-table/joins-table.component';
import { Join } from '../../model/join';
import { Alias } from '../../model/alias';
import { WhereCardComponent } from '../where-card/where-card.component';
import { Condition } from '../../model/condition';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-create-joins-page',
templateUrl: './create-joins-page.component.html',
styleUrls: ['./create-joins-page.component.scss']
})
export class CreateJoinsPageComponent implements OnInit, AfterViewInit{
@ViewChildren(JoinsTableComponent)joinsTableComponent!: QueryList<JoinsTableComponent>;
@ViewChildren(WhereCardComponent)whereCardComponent!: QueryList<WhereCardComponent>;
public allSchema: String[] | undefined;
filterBaseSchema: string = "";
filterBaseTables: string = "";
tablesByBaseSchema: InfoTable[] | undefined;
tablesByBaseSchemaAll: InfoTable[] | undefined;
tablesByBaseSchemaNoViews: InfoTable[] | undefined;
baseTableData: InfoTable | undefined;
isChecked: boolean = false;
requestInfoJoin: Array<JoinsTableComponent> = [];
whereCardList: Array<WhereCardComponent> = [];
baseTableName: string = "";
baseTableNameLowerCase: String = "";
oneJoinRemaining : boolean = true;
baseSchema: string | undefined;
displayJoins: boolean = false;
allData: Join[] = [];
aliasData: Alias[] = [];
selectedAlias: string = "";
passSchema: string = "";
passTable: string = "";
baseAlias: Alias | undefined;
memoAlias: Alias | undefined;
conditionData: Condition[] = [];
newRequestData: string = "SELECT * FROM";
constructor(
private infoColumnService: InfoColumnService,
private infoTableService: InfoTableService,
private clipboard: ClipboardModule,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer
){
this.matIconRegistry.addSvgIcon('copy', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/copy.svg'))
}
ngAfterViewInit(): void {
}
ngOnInit(): void {
this.getSchemas();
this.requestInfoJoin.push(new JoinsTableComponent(this.infoColumnService, this.infoTableService));
}
public refreshRequestData(){
this.newRequestData = "SELECT * FROM";
this.newRequestData += ` ${this.baseSchema}.${this.baseTableName} AS ${this.baseTableNameLowerCase}`;
for (let i = 0; i < this.allData.length; i++) {
const join: Join = this.allData[i];
this.newRequestData += `
${join.joinSpec} ${join.specificSchema}.${join.specificTable} AS ${join.aliasSpecificTable}`;
for (let j = 0; j < join.columns.length; j++) {
const allCol = join.columns[j];
this.newRequestData += `
${allCol.operator} ${join.baseAlias}.${allCol.aliasColumn}=${join.aliasSpecificTable}.${allCol.specColumn}`;
}
}
for (let i = 0; i < this.conditionData.length; i++) {
const cond: Condition = this.conditionData[i];
this.newRequestData += `
${cond.operator} ${cond.alias}.${cond.column} ${cond.condition} ${cond.value}`;
}
}
public getSchemas():void {
this.infoTableService.getSchemas().subscribe(
(response : String[]) => {
this.allSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public useTablesWithBaseSchema (event: any){
this.baseSchema = event.target.value;
this.infoTableService.getTablesBySchema(event.target.value).subscribe(
(response : InfoTable[]) => {
this.tablesByBaseSchemaAll = response;
this.divideTablesAndViews();
this.refreshRequestData();
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public divideTablesAndViews(){
if(this.tablesByBaseSchemaAll){
this.tablesByBaseSchemaNoViews = [];
this.tablesByBaseSchemaAll.forEach((el, i) => {
if(el.nameTable.length < 5){
this.tablesByBaseSchemaNoViews?.push(el);
}
});
}
if(this.isChecked){
this.tablesByBaseSchema = this.tablesByBaseSchemaAll;
} else {
this.tablesByBaseSchema = this.tablesByBaseSchemaNoViews;
}
}
public getNameBaseTable(){
let previousBaseAlias;
if(this.baseSchema && this.baseTableData){
this.baseTableName = this.baseTableData.nameTable;
this.baseTableNameLowerCase = this.baseTableName?.toLowerCase();
this.baseAlias = {
nameSchema: this.baseSchema,
nameTable: this.baseTableName,
nameAlias: this.baseTableNameLowerCase,
}
if(this.aliasData.length != 0){
previousBaseAlias = this.aliasData[0].nameAlias;
for(let iz = this.allData.length; iz >= 0; iz--){
if( this.allData[iz] && this.allData[iz].baseAlias == previousBaseAlias){
this.allData.splice(iz, 1);
this.requestInfoJoin.splice(iz, 1);
this.aliasData.splice(iz, 1);
}
}
if(this.requestInfoJoin.length === 0 ){
this.requestInfoJoin.push(new JoinsTableComponent(this.infoColumnService, this.infoTableService));
}
}
this.aliasData.shift();
this.aliasData.unshift(this.baseAlias);
}
this.refreshRequestData();
}
public addTable(){
this.requestInfoJoin.push(new JoinsTableComponent(this.infoColumnService, this.infoTableService));
this.checkLength();
}
public addCondition(){
this.whereCardList.push(new WhereCardComponent(this.infoColumnService, this.infoTableService));
}
public checkAndRemoveIfAliasWasUse(previousAlias: String){
for(let iz = this.allData.length; iz >= 0; iz--){
if( this.allData[iz] && this.allData[iz].baseAlias == previousAlias){
this.allData.splice(iz, 1);
this.requestInfoJoin.splice(iz, 1);
this.aliasData.splice(iz, 1);
}
}
if(this.conditionData.length != 0){
for(let iy = this.conditionData.length; iy >= 0; iy--){
if( this.conditionData[iy] && this.conditionData[iy].alias == previousAlias){
this.conditionData.splice(iy, 1);
this.whereCardList.splice(iy, 1);
}
}
}
}
public removeTable(i: number){
this.requestInfoJoin.splice(i, 1);
if(this.allData[i]){
const deleteAlias = this.allData[i].aliasSpecificTable;
let aliasSplitName = deleteAlias.slice(0, -1);
this.allData.splice(i, 1);
this.aliasData.shift();
this.checkAndRemoveIfAliasWasUse(deleteAlias);
let indexOfHighest = -1;
let highestValue = 0;
let tempAlias;
let inInclude = false;
if(this.allData.length === 0){
this.requestInfoJoin.push(new JoinsTableComponent(this.infoColumnService, this.infoTableService));
} else {
this.allData.forEach((join, ind) => {
const alias = join.aliasSpecificTable;
if(alias.includes(aliasSplitName.toString())){
tempAlias = alias.slice(-1);
let numTempAlias = +tempAlias;
if(numTempAlias > highestValue){
indexOfHighest = ind;
highestValue = numTempAlias;
}
inInclude = true;
}
})
}
if(inInclude && highestValue != 0){
this.allData[indexOfHighest].aliasSpecificTable = deleteAlias || "";
this.aliasData.splice(i, 1);
this.aliasData[indexOfHighest].nameAlias = deleteAlias;
} else {
this.aliasData.splice(i, 1);
}
if(this.baseAlias){
this.aliasData.unshift(this.baseAlias);
}
}
this.checkLength();
this.aliasData = [...this.aliasData]
this.refreshRequestData();
}
public checkLength(){
console.log(this.requestInfoJoin.length);
if(this.requestInfoJoin.length > 1){
this.oneJoinRemaining = false;
} else {
this.oneJoinRemaining = true;
}
}
public retrieveData(event: Join, index: number){
const tempData = event;
if(this.allData.length != 0 && this.allData[index]){
const antAlias = this.allData[index].aliasSpecificTable;
if(this.allData[index].aliasSpecificTable.includes(tempData.aliasSpecificTable.toString())){
} else {
this.checkAndRemoveIfAliasWasUse(antAlias);
}
}
let switchData = "";
let tempNum = "";
let first = 0;
let tem = 0;
let tempAllData: Join[] = [];
let indexAliasData: Join[] = [];
let noIndexAliasData: Join[] = [];
let alias = "";
let inInclude = false;
let indexOfHighest = -1;
let highestValue= -1;
let tempAlias;
tempAllData = [...this.allData];
if(this.allData.length === 0){
switchData = "emptyData";
} else {
if(this.allData[index]){
if(this.allData.length != 1){
tempAllData.splice(index, 1);
}
tempAllData.forEach((join, i) => {
alias = join.aliasSpecificTable.toString();
if(alias.includes(tempData.aliasSpecificTable.toString())){
indexAliasData.push(tempAllData[i]);
switchData = "indexAlias";
first = 1;
} else {
if(first === 0){
switchData = "indexNoAlias";
}
}
})
} else {
this.allData.forEach((join, i) => {
alias = join.aliasSpecificTable.toString();
if(alias.includes(tempData.aliasSpecificTable.toString())){
noIndexAliasData.push(tempAllData[i]);
switchData = "noIndexAlias";
first = 1
} else {
if(first === 0){
switchData = "noIndexNoAlias";
}
}
})
}
}
switch (switchData) {
case "emptyData":
tempData.aliasSpecificTable = tempData.aliasSpecificTable + "0";
this.allData.push(tempData);
break;
case "indexAlias":
let memo = 0;
const rememberAlias = this.allData[index].aliasSpecificTable;
const aliasSplitName = rememberAlias.slice(0, -1);
if(this.allData[index].aliasSpecificTable == tempData.aliasSpecificTable+"0"){
tempNum = "0"
}
else {
indexAliasData.forEach((jo) => {
const indexAliasName = jo.aliasSpecificTable;
for(let num = 8; num > -1; num--){
if(indexAliasName == (tempData.aliasSpecificTable+num.toString())){
tem = +num + +1;
if(memo < tem){
memo = tem;
tempNum = memo.toString();
}
}
}
})
this.allData.forEach((join, ind) => {
const aliasRem = join.aliasSpecificTable;
if(aliasRem.includes(aliasSplitName.toString())){
tempAlias = aliasRem.slice(-1);
let numTempAlias = +tempAlias;
if(numTempAlias > highestValue){
indexOfHighest = ind;
highestValue = numTempAlias;
}
inInclude = true;
}
})
}
if(inInclude && highestValue != 0){
this.allData[indexOfHighest].aliasSpecificTable = rememberAlias || "";
}
this.allData[index] = tempData;
this.allData[index].aliasSpecificTable = tempData.aliasSpecificTable + tempNum;
break;
case "indexNoAlias":
const rememberIndexAlias = this.allData[index].aliasSpecificTable;
const indexAliasSplitName = rememberIndexAlias.slice(0, -1);
this.allData.forEach((join, ind) => {
const aliasRem = join.aliasSpecificTable;
if(aliasRem.includes(indexAliasSplitName.toString())){
tempAlias = aliasRem.slice(-1);
let numTempAlias = +tempAlias;
if(numTempAlias > highestValue){
indexOfHighest = ind;
highestValue = numTempAlias;
}
inInclude = true;
}
})
if(inInclude && highestValue != 0){
this.allData[indexOfHighest].aliasSpecificTable = rememberIndexAlias || "";
}
this.allData[index] = tempData;
this.allData[index].aliasSpecificTable = tempData.aliasSpecificTable + "0";
break;
case "noIndexAlias":
let memRem = 0;
noIndexAliasData.forEach((jo) => {
const indexAliasName = jo.aliasSpecificTable;
for(let num = 8; num > -1; num--){
if(indexAliasName == (tempData.aliasSpecificTable+num.toString())){
tem = +num + +1;
if(memRem < tem){
memRem = tem;
tempNum = memRem.toString();
}
}
}
})
tempData.aliasSpecificTable = tempData.aliasSpecificTable + tempNum;
this.allData.push(tempData);
break;
case "noIndexNoAlias":
tempData.aliasSpecificTable = tempData.aliasSpecificTable + "0";
this.allData.push(tempData);
break;
}
this.allData = [...this.allData]
if(this.baseAlias){
this.aliasData.splice(0);
this.aliasData.push(this.baseAlias)
this.allData.forEach((all) => {
const ephAlias: Alias = {
nameSchema: all.specificSchema,
nameTable: all.specificTable,
nameAlias: all.aliasSpecificTable
}
this.aliasData.push(ephAlias);
});
}
this.aliasData = [...this.aliasData];
this.refreshRequestData();
}
public removeWhereCard(i: number){
this.whereCardList.splice(i, 1);
if(this.conditionData[i]){
this.conditionData.splice(i, 1);
}
if(this.conditionData[0]){
this.conditionData[0].operator = "WHERE";
}
this.refreshRequestData();
}
public getConditionData(event: String[], index: number){
const tempCondition: Condition = {
operator: event[0],
alias: event[1],
column: event[2],
condition: event[3],
value: event[4]
}
if(this.conditionData[index]){
this.conditionData[index] = tempCondition;
} else {
this.conditionData.push(tempCondition);
}
this.refreshRequestData();
}
public checkValue(event: any){
this.tablesByBaseSchema = this.isChecked ?
this.tablesByBaseSchemaAll
: this.tablesByBaseSchemaNoViews;
}
}

@ -0,0 +1,55 @@
<div [ngClass]="{'normalCard' : goodAlias, 'alternativeCard' : !goodAlias}" class="p-4">
<div class="font-bold text-xl text-left mb-2">Selectionner les informations de la jointure :</div>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
<div>
<label class="text-black text-left">Jointures : </label>
<select [(ngModel)]="joinData" (change)="verifyAndSendData()" size="4" class="my-2 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let join of alljoins" [ngValue]="join">{{ join }}</option>
</select>
</div>
<div>
<label class="text-black text-left">Schémas : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterBaseSchema" placeholder="Rechercher...">
<select (change)="useTablesWithSpecificSchema($event)" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of allSchema | filter : filterBaseSchema" value="{{ schema }}">{{ schema }}</option>
</select>
</div>
<div>
<label class="text-black text-left">Tables : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterBaseTables" placeholder="Rechercher...">
<select [(ngModel)]="specificTableData" (change)="getSpecificColumns()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of tablesByBaseSchema | filter : filterBaseTables" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
</div>
<div *ngIf="allAlias">
<label class="text-black text-left">Alias(Base) : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterAlias" placeholder="Rechercher...">
<select [(ngModel)]="baseAlias" (change)="getBaseColumns()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let alias of tempAliasData | filter : filterAlias" [ngValue]="alias">{{ alias.nameAlias }}</option>
</select>
</div>
</div>
<div *ngIf="tablesByBaseSchema" class="mb-2">
<input type="checkbox" [(ngModel)]="isChecked" (change)="checkValue(isChecked?'true':'false')" class="w-4 h-4 text-orange-400 bg-slate-400 border-slate-200 rounded focus:ring-orange-400 focus:ring-2"/>
<label class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Afficher aussi les Vues</label>
</div>
<div *ngFor="let cols of columnsInfo, index as ind">
<app-columns-card [baseColumns]="baseColumns" [specificColumns]="specificColumns" [childIndex]="ind" (selectedColumn)="retrieveColumnsData($event, ind)"></app-columns-card>
<div class="inline-flex space-x-4">
<button (click)="addSelectColumns()" type="button" class="btn-grad-green-teal mr-2 my-2">+</button>
<button *ngIf="columnsInfo.length > 1" (click)="removeSelectColumns(ind)" type="button" class="btn-grad-red-rose my-2">-</button>
</div>
</div>
</div>

@ -0,0 +1,25 @@
// rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2 my-2
.normalCard {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
background-color: rgb(241 245 249);
border-style: solid;
border-color: rgb(251 146 60);
border-width: 2px;
margin-top: 0.5rem; /* 8px */
margin-bottom: 0.5rem; /* 8px */
}
.alternativeCard {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
background-color: rgb(71 85 105);
border-style: solid;
border-color: rgb(251 146 60);
border-width: 2px;
margin-top: 0.5rem; /* 8px */
margin-bottom: 0.5rem; /* 8px */
}

@ -0,0 +1,198 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import { InfoColumn } from '../../model/info-column';
import { InfoTable } from '../../model/info-table';
import { InfoColumnService } from '../../service/info-column.service';
import { InfoTableService } from '../../service/info-table.service';
import { Alias } from '../../model/alias';
import { Join } from '../../model/join';
import { ColumnData } from '../../model/column-data';
import { ColumnsCardComponent } from '../columns-card/columns-card.component';
@Component({
selector: 'app-joins-table',
templateUrl: './joins-table.component.html',
styleUrls: ['./joins-table.component.scss']
})
export class JoinsTableComponent implements OnInit, OnChanges{
@ViewChildren(ColumnsCardComponent)joinsColumnsComponent!: QueryList<ColumnsCardComponent>;
@Input() allAlias: Alias[] = [];
@Input() passJoinData: Join | undefined;
@Output() data = new EventEmitter<Join>();
@Output() selectedAlias = new EventEmitter<String>();
public allSchema: String[] | undefined;
filterBaseSchema: string = "";
filterBaseTables: string = "";
isChecked: boolean = false;
filterAlias: string = "";
specificTableData: InfoTable | undefined;
tablesByBaseSchema: InfoTable[] | undefined;
tablesByBaseSchemaAll: InfoTable[] | undefined;
tablesByBaseSchemaNoViews: InfoTable[] | undefined;
joinData: string | undefined;
alljoins: string[] = ['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'FULL JOIN'];
selectedSchema: string | undefined;
columnsForSpecificTable: InfoColumn[] | undefined;
baseColumns: InfoColumn[] | undefined;
specificColumns: InfoColumn[] | undefined;
specificSchema: string | undefined;
specificTable: string | undefined;
baseAlias: Alias | undefined;
myJoinData: Join | undefined;
isGood: boolean = true;
positionIndex: number = 0;
tempAliasData: Alias[] = [];
goodAlias = true;
columnsInfo: Array<ColumnsCardComponent> = [];
columnsData: ColumnData[] = [];
constructor(
private infoColumnService: InfoColumnService,
private infoTableService: InfoTableService
){}
ngOnInit(): void {
this.getSchemas();
this.getBaseColumns();
this.columnsInfo.push(new ColumnsCardComponent());
}
ngOnChanges(): void {
this.getBaseColumns();
this.verifyAlias();
}
public verifyAlias(){
this.tempAliasData = this.allAlias;
if(this.passJoinData){
this.tempAliasData = [];
const myAlias = this.passJoinData.aliasSpecificTable;
this.allAlias.forEach((alias, i) =>{
if(alias.nameAlias !== myAlias){
this.tempAliasData.push(alias);
}
})
}
}
public getSchemas():void {
this.infoTableService.getSchemas().subscribe(
(response : String[]) => {
this.allSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public useTablesWithSpecificSchema (event: any){
this.specificSchema = event.target.value;
this.infoTableService.getTablesBySchema(event.target.value).subscribe(
(response : InfoTable[]) => {
this.tablesByBaseSchemaAll = response;
this.divideTablesAndViews();
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public divideTablesAndViews(){
if(this.tablesByBaseSchemaAll){
this.tablesByBaseSchemaNoViews = [];
this.tablesByBaseSchemaAll.forEach((el, i) => {
if(el.nameTable.length < 5){
this.tablesByBaseSchemaNoViews?.push(el);
}
});
}
if(this.isChecked){
this.tablesByBaseSchema = this.tablesByBaseSchemaAll;
} else {
this.tablesByBaseSchema = this.tablesByBaseSchemaNoViews;
}
}
public getBaseColumns():void {
if(this.baseAlias){
this.infoColumnService.getSelectedColumns(this.baseAlias.nameSchema, this.baseAlias.nameTable).subscribe(
(response : InfoColumn[]) => {
this.baseColumns = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public getSpecificColumns():void {
if(this.specificTableData)
this.specificTable = this.specificTableData.nameTable;
if(this.specificSchema && this.specificTable){
this.infoColumnService.getSelectedColumns(this.specificSchema, this.specificTable).subscribe(
(response : InfoColumn[]) => {
this.specificColumns = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public verifyAndSendData(){
if(this.specificSchema && this.specificTable && this.joinData && this.baseAlias && this.columnsData){
const tempLower = this.specificTable.toLowerCase();
this.myJoinData = {
joinSpec: this.joinData,
specificSchema: this.specificSchema,
specificTable: this.specificTable,
baseAlias: this.baseAlias.nameAlias,
columns: this.columnsData,
aliasSpecificTable: tempLower,
}
this.data.emit(this.myJoinData);
}
}
public addSelectColumns(){
this.columnsInfo.push(new ColumnsCardComponent());
}
public removeSelectColumns(index: number){
this.columnsInfo.splice(index, 1)
if(this.columnsData[index]){
this.columnsData.splice(index, 1);
}
if(this.columnsData[0]){
this.columnsData[0].operator = "ON";
}
this.verifyAndSendData()
}
public retrieveColumnsData(data: ColumnData, index: number){
if(this.columnsData.length === 0){
this.columnsData.push(data);
}
if(this.columnsData[index]){
this.columnsData[index] = data;
} else {
this.columnsData.push(data);
}
this.verifyAndSendData();
}
public checkValue(event: any){
if(this.isChecked){
this.tablesByBaseSchema = this.tablesByBaseSchemaAll;
} else {
this.tablesByBaseSchema = this.tablesByBaseSchemaNoViews;
}
}
}

@ -0,0 +1,41 @@
<div class="rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2 my-2 p-4">
<div class="font-bold text-xl text-left mb-2">Selectionner les informations de la Condition :</div>
<div class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-5 gap-4">
<div *ngIf="childIndex != 0">
<label class="text-black text-left">Opérateurs : </label>
<select [(ngModel)]="operator" (change)="verifyAndSendConditionData()" size="4" class="my-2 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let ope of operatorList" [ngValue]="ope">{{ ope }}</option>
</select>
</div>
<div>
<label class="text-black text-left">Alias : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterAlias" placeholder="Rechercher...">
<select [(ngModel)]="baseAlias" (change)="getColumns()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let alias of allAlias | filter : filterAlias" [ngValue]="alias">{{ alias.nameAlias }}</option>
</select>
</div>
<div>
<label class="text-black text-left">Colonnes : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterColumn" placeholder="Rechercher...">
<select [(ngModel)]="baseColumnData" (change)="verifyAndSendConditionData()" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let col of baseColumns | filter : filterColumn" id="{{ col.id }}" [ngValue]="col">{{ col.nameColumn }} ({{col.columnText}})</option>
</select>
</div>
<div *ngIf="allAlias">
<label class="text-black text-left">Conditions : </label>
<input type="text" (change)="verifyAndSendConditionData()" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="conditionOperator" placeholder="Condition...">
</div>
<div>
<label class="text-black text-left">Valeurs : </label>
<input type="text" (change)="verifyAndSendConditionData()" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="conditionValue" placeholder="Valeur...">
</div>
</div>
</div>

@ -0,0 +1,54 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Alias } from '../../model/alias';
import { InfoColumn } from '../../model/info-column';
import { InfoColumnService } from '../../service/info-column.service';
import { InfoTableService } from '../../service/info-table.service';
@Component({
selector: 'app-where-card',
templateUrl: './where-card.component.html',
styleUrls: ['./where-card.component.scss']
})
export class WhereCardComponent {
@Input() allAlias: Alias[] = [];
@Input() childIndex: number | undefined;
@Output() conditionData = new EventEmitter<String[]>();
operator: string | undefined;
operatorList: string[] = ["AND", "OR"];
conditionValue: any;
conditionOperator: any;
filterAlias: string = "";
filterColumn: string = "";
baseAlias: Alias | undefined;
baseColumns: InfoColumn[] | undefined;
baseColumnData: InfoColumn | undefined;
constructor(
private infoColumnService: InfoColumnService,
private infoTableService: InfoTableService
){}
public getColumns():void {
if(this.baseAlias){
this.infoColumnService.getSelectedColumns(this.baseAlias.nameSchema, this.baseAlias.nameTable).subscribe(
(response : InfoColumn[]) => {
this.baseColumns = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public verifyAndSendConditionData(){
if(this.childIndex === 0){
this.operator = "WHERE";
}
if(this.operator && this.baseAlias && this.baseColumnData && this.conditionOperator && this.conditionValue){
this.conditionData.emit([this.operator, this.baseAlias.nameAlias, this.baseColumnData.nameColumn, this.conditionOperator, this.conditionValue]);
}
}
}

@ -0,0 +1,20 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Script } from '../model/script';
@Pipe({
name: 'filterscript',
pure: false
})
export class CustomFilterScript implements PipeTransform {
transform(items: Script[], filter: string): any {
if (!items || !filter) {
return items;
}
// filter items array, items which match and return true will be
// kept, false will be filtered out
if(filter){
return items.filter(item => item.name.toLowerCase().indexOf(filter) !== -1);
}
}
}

@ -0,0 +1,36 @@
import { InfoTable } from '../model/info-table';
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filtertable',
pure: false
})
export class CustomFilterTable implements PipeTransform {
transform(items: InfoTable[], filter: string): any {
if (!items || !filter) {
return items;
}
// Filter items array, keeping items that match the filter
const filteredItems = items.filter(item => item.nameTable.toLowerCase().indexOf(filter.toLowerCase()) !== -1);
// Sort the filtered items by the length of the text in ascending order
const sortedItems = filteredItems.sort((a, b) => {
if (a.nameTable.length < b.nameTable.length) {
return -1;
} else if (a.nameTable.length > b.nameTable.length) {
return 1;
} else {
return 0;
}
});
return sortedItems;
}
}

@ -0,0 +1,62 @@
import { Pipe, PipeTransform, Injectable } from "@angular/core";
@Pipe({
name: "filter",
pure: false
})
@Injectable()
export class Custom_Ng2SearchPipe implements PipeTransform {
transform(items: any, term: string, excludes: any = []): any {
if (!term || !items) return items;
return Custom_Ng2SearchPipe.filter(items, term, excludes);
}
static filter(
items: Array<{ [key: string]: any }>,
term: string,
excludes: any
): Array<{ [key: string]: any }> {
const toCompare = term.toLowerCase();
function checkInside(item: any, term: string) {
if (
typeof item === "string" &&
item
.toString()
.toLowerCase()
.includes(toCompare)
) {
return true;
}
for (let property in item) {
if (
item[property] === null ||
item[property] == undefined ||
excludes.includes(property)
) {
continue;
}
if (typeof item[property] === "object") {
if (checkInside(item[property], term)) {
return true;
}
} else if (
item[property]
.toString()
.toLowerCase()
.includes(toCompare)
) {
return true;
}
}
return false;
}
return items.filter(function (item) {
return checkInside(item, term);
});
}
}

@ -0,0 +1,42 @@
import { Directive, EventEmitter, HostBinding, HostListener, Output } from '@angular/core';
@Directive({
selector: '[appDnd]'
})
export class DndDirective {
@HostBinding('class.animate-pulse') fileOver: boolean | undefined;
@Output() fileDropped = new EventEmitter<any>();
@Output() textDrop = new EventEmitter<string[]>();
listOfContent: string[] = [];
// Dragover listener
@HostListener('dragover', ['$event']) onDragOver(evt: any) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = true;
}
// Dragleave listener
@HostListener('dragleave', ['$event']) public onDragLeave(evt: any) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = false;
}
// Drop listener
@HostListener('drop', ['$event']) public ondrop(evt: any) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = false;
let files = evt.dataTransfer.files;
if (files.length > 0) {
this.fileDropped.emit(files);
}
}
constructor(){
}
}

@ -0,0 +1,141 @@
<div class="my-4 grid sm:grid-cols-1 md:grid-cols-2 gap-5">
<!--Card-->
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 py-4">
<div class="font-bold text-xl text-center">Selectionner un Schéma et une Table :</div>
<div class="bg-orange-400 w-full h-2 rounded-lg overflow-hidden my-2"></div>
<label class="text-black">Schémas : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterFirstSchema" placeholder="Rechercher...">
<select (change)="useTablesWithFirstSchema($event)" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of allSchema | filter : filterFirstSchema" value="{{ schema }}">{{ schema }}</option>
</select>
<div class="bg-orange-400 w-full h-2 rounded-lg overflow-hidden my-2"></div>
<div *ngIf="tablesByFirstSchema">
<label class="text-black">Tables : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterFirstTable" placeholder="Rechercher...">
<select [(ngModel)]="firstTableData" (change)="getColumnsForJoinTwo()" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of tablesByFirstSchema | filtertable : filterFirstTable" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
</div>
</div>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 py-4 ">
<div class="font-bold text-xl text-center">Selectionner un Schéma et une Table :</div>
<div class="bg-orange-400 w-full h-2 rounded-lg overflow-hidden my-2"></div>
<label class="text-black">Schémas : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterSecondSchema" placeholder="Rechercher...">
<select (change)="useTablesWithSecondSchema($event)" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of allSchema | filter : filterSecondSchema" value="{{ schema }}">{{ schema }}</option>
</select>
<div class="bg-orange-400 w-full h-2 rounded-lg overflow-hidden my-2"></div>
<div *ngIf="tablesBySecondSchema">
<label class="text-black">Tables : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterSecondTable" placeholder="Rechercher...">
<select [(ngModel)]="secondTableData" (change)="getColumnsForJoinTwo()" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of tablesBySecondSchema | filtertable : filterSecondTable" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
</div>
</div>
</div>
</div>
<div *ngIf="!columnIsFull && infoColumn" class="my-4 grid grid-cols-1 sm:grid-cols-4 gap-5">
<!--Card-->
<div class="sm:col-start-2 sm:col-span-2 rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 p-4 text-center">
<label class="text-red-600">ATTENTION : Il n'y a pas de colonnes en commun entre les deux tables sélectionnées.</label>
</div>
</div>
</div>
<div *ngIf="columnIsFull && infoColumn" class="my-4 grid grid-cols-1 sm:grid-cols-4 gap-5">
<!--Card-->
<div class="sm:col-start-2 sm:col-span-2 rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 pt-4 ">
<label class="text-black mb-4">Colonne(s) en commun : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterColumn" placeholder="Rechercher...">
<select (change)="displayColumnsInformations($event)" size="3" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let column of infoColumn | filter: filterColumn" id="{{ column.id }}" value="{{ column.id }}">{{ column.nameColumn }}</option>
</select>
</div>
<!--Card-->
<div *ngIf="displayColumns" class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="bg-orange-400 w-full h-2 overflow-hidden my-2"></div>
<div class="px-6 pb-4 grid justify-items-center">
<div class="font-bold text-xl mb-2">Colonne : {{ displayColumns.nameColumn }}</div>
<p class="text-lg">Data Type : {{ displayColumns.dataType }}</p>
<p class="text-lg">Taille : {{ displayColumns.lengthColumn }}</p>
<p class="text-lg">Description : {{ displayColumns.columnText }}</p>
</div>
</div>
</div>
</div>
<div *ngIf="displayColumns" class="my-4 grid sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
<!--Card-->
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="font-bold text-xl text-center pt-2">INNER JOIN</div>
<div class="px-2 py-2">
<textarea rows="4" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500">
SELECT {{ nameFirstTableLowerCase }}.*, {{ nameSecondTableLowerCase }}.*
FROM {{firstSchema}}.{{nameFirstTable}} AS {{ nameFirstTableLowerCase }}
INNER JOIN {{secondSchema}}.{{nameSecondTable}} AS {{ nameSecondTableLowerCase }}
ON {{ nameFirstTableLowerCase }}.{{ displayColumns.nameColumn }}={{ nameSecondTableLowerCase }}.{{ displayColumns.nameColumn }}</textarea>
</div>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="font-bold text-xl text-center pt-2">FULL JOIN</div>
<div class="px-2 py-2">
<textarea rows="4" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500">
SELECT {{ nameFirstTableLowerCase }}.*, {{ nameSecondTableLowerCase }}.*
FROM {{firstSchema}}.{{nameFirstTable}} AS {{ nameFirstTableLowerCase }}
FULL JOIN {{secondSchema}}.{{nameSecondTable}} AS {{ nameSecondTableLowerCase }}
ON {{ nameFirstTableLowerCase }}.{{ displayColumns.nameColumn }}={{ nameSecondTableLowerCase }}.{{ displayColumns.nameColumn }}</textarea>
</div>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="font-bold text-xl text-center pt-2">LEFT JOIN</div>
<div class="px-2 py-2">
<textarea rows="4" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500">
SELECT {{ nameFirstTableLowerCase }}.*, {{ nameSecondTableLowerCase }}.*
FROM {{firstSchema}}.{{nameFirstTable}} AS {{ nameFirstTableLowerCase }}
LEFT JOIN {{secondSchema}}.{{nameSecondTable}} AS {{ nameSecondTableLowerCase }}
ON {{ nameFirstTableLowerCase }}.{{ displayColumns.nameColumn }}={{ nameSecondTableLowerCase }}.{{ displayColumns.nameColumn }}</textarea>
</div>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="font-bold text-xl text-center pt-2">RIGHT JOIN</div>
<div class="px-2 py-2">
<textarea rows="4" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500">
SELECT {{ nameFirstTableLowerCase }}.*, {{ nameSecondTableLowerCase }}.*
FROM {{firstSchema}}.{{nameFirstTable}} AS {{ nameFirstTableLowerCase }}
RIGHT JOIN {{secondSchema}}.{{nameSecondTable}} AS {{ nameSecondTableLowerCase }}
ON {{ nameFirstTableLowerCase }}.{{ displayColumns.nameColumn }}={{ nameSecondTableLowerCase }}.{{ displayColumns.nameColumn }}</textarea>
</div>
</div>
</div>

@ -0,0 +1,130 @@
import { Component, OnInit } from '@angular/core';
import { InfoColumnService } from '../../service/info-column.service';
import { InfoTableService } from '../../service/info-table.service';
import { HttpErrorResponse } from '@angular/common/http';
import { InfoTable } from '../../model/info-table';
import { InfoColumn } from '../../model/info-column';
@Component({
selector: 'app-joins-page',
templateUrl: './joins-page.component.html',
styleUrls: ['./joins-page.component.scss']
})
export class JoinsPageComponent implements OnInit{
public allSchema: String[] | undefined;
filterFirstSchema: string = "";
filterSecondSchema: string = "";
filterFirstTable: string = "";
filterSecondTable: string = "";
filterColumn: string = "";
public firstSchema: string | undefined;
public secondSchema: string | undefined;
public tablesByFirstSchema: InfoTable[] | undefined;
public tablesBySecondSchema: InfoTable[] | undefined;
firstTableData: InfoTable | undefined;
secondTableData: InfoTable | undefined;
public infoColumn: InfoColumn[] | undefined;
public columnIsFull: boolean = false;
public endRequestForColumns: boolean = false;
public displayColumns: InfoColumn | undefined;
public nameFirstTableLowerCase: string = "";
public nameSecondTableLowerCase: string = "";
public nameFirstTable: string = "";
public nameSecondTable: string = "";
public tables: string[] | undefined;
public schemas: string[] | undefined;
constructor(
private infoColumnService: InfoColumnService,
private infoTableService: InfoTableService
){}
ngOnInit(): void {
this.getSchemas();
}
public getSchemas():void {
this.infoTableService.getSchemas().subscribe(
(response : String[]) => {
this.allSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public useTablesWithFirstSchema (event: any){
this.firstSchema = event.target.value;
this.displayColumns = undefined;
this.columnIsFull = false;
this.infoColumn = undefined;
this.infoTableService.getTablesBySchema(event.target.value).subscribe(
(response : InfoTable[]) => {
this.tablesByFirstSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public useTablesWithSecondSchema (event: any){
this.columnIsFull = false;
this.secondSchema = event.target.value;
this.displayColumns = undefined;
this.infoColumn = undefined;
this.infoTableService.getTablesBySchema(event.target.value).subscribe(
(response : InfoTable[]) => {
this.tablesBySecondSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public displayColumnsInformations (event: any){
if(this.firstTableData && this.secondTableData){
this.nameFirstTableLowerCase = this.firstTableData.nameTable.toLowerCase();
this.nameSecondTableLowerCase = this.secondTableData.nameTable.toLowerCase();
this.nameFirstTable = this.firstTableData.nameTable;
this.nameSecondTable = this.secondTableData.nameTable;
this.infoColumnService.getColumn(event.target.value).subscribe(
(response : InfoColumn) => {
this.displayColumns = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public getColumnsForJoinTwo (){
this.displayColumns = undefined;
if(this.firstTableData && this.secondTableData && this.firstSchema && this.secondSchema){
this.schemas = [this.firstSchema, this.secondSchema];
this.tables = [this.firstTableData.nameTable, this.secondTableData.nameTable];
this.infoColumnService.getColumnsForJoinTwo(this.tables, this.schemas).subscribe(
(response : InfoColumn[]) => {
this.infoColumn = response;
if(this.infoColumn?.length === 0){
this.columnIsFull = false;
} else {
this.columnIsFull = true;
}
},
(error: HttpErrorResponse) => {
alert(error.message);
}
);
}
}
}

@ -0,0 +1,24 @@
<div class="mx-auto">
<img src="assets/images/logo-apsidetop-blanc.png" alt="Apside logo" class="mx-auto">
<div class="text-center text-lg text-white">Produit créé par APSIDE TOP</div>
</div>
<div class="text-center text-white my-4">
<p>Cette application, AssistDB a pour objectif de vous aider à rechercher des informations, créer des jointures et gérer un ensemble de scripts et de requêtes de manière centralisée entre tous les membres de l'équipe.</p>
<p class="mt-4">L'application est composée de plusieurs pages correspondant à des fonctionnalités bien disctintes :</p>
</div>
<div class="grid md:grid-cols-2 gap-4">
<div class="text-justify text-white">
<p class="mt-2">-|- La page de Recherche : Permet de récupérer les informations d'une ou plusieurs colonnes spécifiques.</p>
<p class="mt-2">-|- La page de Jointures : Permet de générer automatiquement des jointures entre des tables possédant des colonnes identiques.</p>
</div>
<div class="text-justify text-white">
<p class="mt-2">-|- La page de Création : Permet de créer des requêtes SQL complexes avec des jointures sur plusieurs colonnes ainsi que des conditions.</p>
<p class="mt-2">-|- La page de Gestionnaire : Permet de voir, ajouter, modifier et supprimer des scripts ou requêtes. Un système de Tags permet aussi une classification et une catégorisation complète. Les tags peuvent aussi être ajoutés, modifiés et supprimés. L'ensemble de cette page est actualisé et centralisé via GitHub pour l'ensemble des utilisateurs de l'application.
</p>
</div>
</div>

@ -0,0 +1,11 @@
.landing-block {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.landing-links {
width: 100%;
text-align: center;
}

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-landing-page',
templateUrl: './landing-page.component.html',
styleUrls: ['./landing-page.component.scss']
})
export class LandingPageComponent {
}

@ -0,0 +1,72 @@
<div class="flex sm:flex-nowrap flex-wrap sm:space-x-4 my-4">
<div class="w-full sm:w-2/3 h-full text-center rounded-lg overflow-hidden shadow-lg bg-white p-4 ">
<div class="flex">
<button (click)="isNewFile()" class="btn-grad-purple-pink m-2" >Nouveau</button>
<button (click)="isExistingFile()" class="btn-grad-purple-pink m-2" >Fichier Existant</button>
</div>
<div class="rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2 p-4">
<div *ngIf="newFile">
<div class="font-bold text-xl text-left mb-2">Veuillez rentrer le contenu du Script : </div>
<textarea [(ngModel)]="newFileData" rows="20" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-orange-400 focus:border-orange-400">
</textarea>
</div>
<div *ngIf="!newFile">
<div class="py-2 relative rounded-lg overflow-hidden bg-slate-900 border-solid border-orange-400 border-2 text-center text-white" appDnd (fileDropped)="onFileDropped($event)" >
<input type="file" multiple class=" z-10 opacity-0 absolute w-full h-full top-0 left-0"/>
<mat-icon class="flex !h-20 !w-20" svgIcon="upload"></mat-icon>
<div>Glisser / Déposer le script ici</div>
</div>
<div *ngIf="files.length > 0" class="my-2">
<div *ngFor="let file of files, index as i" class="mb-2 p-2 rounded-lg overflow-hidden bg-slate-200 border-solid border-orange-400 border-2" (click)="displayText(i)">
<div class="grid grid-cols-1 sm:grid-cols-2 ">
<div class="flex justify-start ">
<div class="font-bold mr-2">{{ file?.name }}</div>
<div>Taille: {{ formatBytes(file.size) }}</div>
</div>
<div class="flex justify-end my-auto">
<mat-icon (click)="deleteFile(i)" class="text-red-600 mx-2 hover:cursor-pointer" svgIcon="trash"></mat-icon>
</div>
</div>
</div>
</div>
<div class="font-bold text-xl text-left mb-2">Contenu du Script : </div>
<textarea [(ngModel)]="existingFileData" rows="20" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-orange-400 focus:border-orange-400">
</textarea>
</div>
</div>
</div>
<div class="w-full mt-2 sm:mt-0 sm:w-1/3 h-full rounded-lg shadow-lg bg-white p-4 ">
<div class="text-xl font-bold pb-2 text-center">Informations du Script :</div>
<div class="font-bold text-lg">Nom du Script :</div>
<input type="text" class="block w-full my-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="nameOfFile" placeholder="Tapez ici...">
<div class="font-bold text-lg">Type d'extension :</div>
<select [(ngModel)]="nameOfExtension" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none" required>
<option [ngValue]="undefined" disabled>Sélectionner l'extension</option>
<option *ngFor="let ext of listOfExtension" value="{{ ext }}">{{ ext }}</option>
</select>
<div class="font-bold text-lg">Description du Script :</div>
<textarea type="text" class="block w-full my-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="description" placeholder="Description..."></textarea>
<div class="font-bold text-lg">Tags associés :</div>
<ng-multiselect-dropdown class="overflow-y-auto"
[placeholder]="'Selectionner les Tags'"
[settings]="dropdownSettings"
[data]="dropdownTagList"
[(ngModel)]="selectedTagList"
(onSelect)="onItemSelect($event)"
(onSelectAll)="onSelectAll($event)"
(onDeSelect)="onItemDeselect($event)"
(onDeSelectAll)="onDeselectAll($event)"
>
</ng-multiselect-dropdown>
<button (click)="addScriptToCentral()" type="button" class="btn-grad-teal-lime mr-2 mb-2 mt-4">Ajouter le Script</button>
</div>
</div>

@ -0,0 +1,183 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { Observable, Subject } from 'rxjs';
import { Script } from '../../model/script';
import { Router, RouterModule } from '@angular/router';
import { ScriptService } from '../../service/script.service';
import { Tag } from '../../model/tag';
import { TagsService } from '../../service/tags.service';
@Component({
selector: 'app-add-script',
templateUrl: './add-script.component.html',
styleUrls: ['./add-script.component.scss']
})
export class AddScriptComponent {
nameOfFile : string | undefined;
nameOfExtension : string | undefined;
listOfExtension : string[] = ["sql", "ps1", "txt", "bat"];
newFile : boolean = false;
newFileData: string | undefined;
existingFileData: string | undefined;
files: File[] = [];
contentOfFiles: string[] = [];
scriptToSend: Script | undefined;
dropdownTagList: Tag[] = [];
selectedTagList: Tag[] = [];
dropdownSettings = {};
linkId: number | undefined;
description: string | undefined;
constructor(
private scriptService: ScriptService,
private tagService: TagsService,
private router: Router,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer){
this.matIconRegistry.addSvgIcon('upload', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/upload.svg')),
this.matIconRegistry.addSvgIcon('trash', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/trash.svg'))
const navigation = this.router.getCurrentNavigation();
if(navigation){
const state = navigation.extras.state as {
nextId: number
};
this.linkId = state.nextId;
}
}
ngOnInit(): void{
this.retrieveTags();
this.dropdownSettings = {
singleSelection: false,
idField: "tagId",
textField: "nameTag",
selectAllText: "Tout Sélectionner",
unSelectAllText: "Déselectionner tout",
itemsShowLimit: 3,
allowSearchFilter: true
};
}
public retrieveTags(){
this.tagService.getAllTags().subscribe(
(response: Tag[]) => {
this.dropdownTagList = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
public isNewFile (){
this.newFile = true;
}
public isExistingFile (){
this.newFile = false;
}
public deleteFile(index: number) {
this.files.splice(index, 1);
if(index == 0){
this.newFileData = undefined;
}
}
public onFileDropped(event: any){
this.readFile(event[0]).subscribe((output) => {
this.existingFileData = output;
})
for(const item of event){
this.files.push(item);
}
}
public displayText(index: number){
this.readFile(this.files[index]).subscribe((output) => {
this.existingFileData = output;
})
}
readFile(file: File): Observable<string> {
const sub = new Subject<string>();
var reader = new FileReader();
reader.onload = () => {
const content: string = reader.result as string;
sub.next(content);
sub.complete();
};
reader.readAsText(file);
return sub.asObservable();
}
public renderFileContent( value: string[] ) : void {
this.newFileData = value[0];
}
public formatBytes(bytes: number) {
if (bytes === 0) {
return '0 Bytes';
}
const k = 1024;
const dm = 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
public addScriptToCentral(){
if(this.nameOfExtension && this.nameOfFile && this.description && this.linkId && this.selectedTagList && (this.existingFileData || this.newFileData)){
let listOfTags: String[] = []
this.selectedTagList.forEach(tag => {
listOfTags.push(tag.nameTag);
});
const fullName = this.nameOfFile + "." + this.nameOfExtension;
let tempData: String = "";
if(this.newFile && this.newFileData){
tempData = this.newFileData
} else if(!this.newFile && this.existingFileData){
tempData = this.existingFileData
}
this.scriptToSend = {
extension: this.nameOfExtension,
name: fullName,
data: tempData,
}
this.scriptService.addOneScript(this.scriptToSend, this.description, listOfTags, this.linkId).subscribe(
() => {
this.router.navigateByUrl('scriptmanagement');
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
}
onItemSelect(item: any) {
}
onSelectAll(items: any) {
this.selectedTagList = items;
}
onItemDeselect(item: any){
}
onDeselectAll(items: any){
this.selectedTagList = [];
}
}

@ -0,0 +1,40 @@
<div class="flex sm:flex-nowrap flex-wrap sm:space-x-4 my-4">
<div class="w-full sm:w-2/3 h-full text-center rounded-lg overflow-hidden shadow-lg bg-white p-4 ">
<div class="rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2">
<div class="p-4">
<div class="font-bold text-xl text-left mb-2">Contenu du Script : </div>
<textarea [(ngModel)]="newData" rows="20" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-orange-400 focus:border-orange-400">
</textarea>
</div>
</div>
</div>
<div class="w-full mt-2 sm:mt-0 sm:w-1/3 h-full rounded-lg shadow-lg bg-white p-4 ">
<div class="text-xl font-bold pb-2 text-center">Informations du Script :</div>
<div class="font-bold text-lg">Nom du Script :</div>
<input [(ngModel)]="newName" type="text" class="block w-full my-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Tapez ici...">
<div class="font-bold text-lg">Type d'extension :</div>
<select [(ngModel)]="newExtension" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none" required>
<option [ngValue]="undefined" disabled>Sélectionner l'extension</option>
<option *ngFor="let ext of listOfExtension" value="{{ ext }}">{{ ext }}</option>
</select>
<div class="font-bold text-lg">Description du Script :</div>
<textarea type="text" class="block w-full my-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="description" placeholder="Description..."></textarea>
<div class="font-bold text-lg">Tags associés :</div>
<ng-multiselect-dropdown class="overflow-y-auto"
[placeholder]="'Selectionner les Tags'"
[settings]="dropdownSettings"
[data]="dropdownTagList"
[(ngModel)]="selectedTagList"
(onSelect)="onItemSelect($event)"
(onSelectAll)="onSelectAll($event)"
(onDeSelect)="onItemDeselect($event)"
(onDeSelectAll)="onDeselectAll($event)"
>
</ng-multiselect-dropdown>
<button (click)="editScript()" type="button" class="btn-grad-teal-lime mr-2 mb-2 mt-4">Modifier le Script</button>
</div>
</div>

@ -0,0 +1,109 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ScriptService } from '../../service/script.service';
import { Tag } from '../../model/tag';
import { TagsService } from '../../service/tags.service';
@Component({
selector: 'app-edit-script',
templateUrl: './edit-script.component.html',
styleUrls: ['./edit-script.component.scss']
})
export class EditScriptComponent {
defaultName: string | undefined;
newName: string | undefined;
newExtension: string | undefined;
newData: string | undefined;
editedId: number | undefined;
listOfExtension : String[] = ["sql", "ps1", "txt", "bat"];
dropdownTagList: Tag[] = [];
selectedTagList: Tag[] = [];
dropdownSettings = {};
description: string | undefined;
constructor(private router: Router,
private scriptService: ScriptService,
private tagService: TagsService,){
const navigation = this.router.getCurrentNavigation();
if(navigation){
const state = navigation.extras.state as {
name: string,
extension: string,
data: string,
linkId: number,
tags: Tag[],
description: string,
};
const splitString = state.name.split(".");
this.defaultName = state.name;
this.newName = splitString[0];
this.newExtension = state.extension;
this.newData = state.data;
this.editedId = state.linkId;
this.selectedTagList = state.tags;
this.description = state.description;
}
}
ngOnInit(): void{
this.retrieveTags();
this.dropdownSettings = {
singleSelection: false,
idField: "tagId",
textField: "nameTag",
selectAllText: "Tout Sélectionner",
unSelectAllText: "Déselectionner tout",
itemsShowLimit: 3,
allowSearchFilter: true
};
}
public retrieveTags(){
this.tagService.getAllTags().subscribe(
(response: Tag[]) => {
this.dropdownTagList = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
public editScript(){
if(this.newExtension && this.editedId && this.newName && this.description && this.newData && this.defaultName){
const fullName = this.newName + "." + this.newExtension;
let listOfTags: String[] = []
this.selectedTagList.forEach(tag => {
listOfTags.push(tag.nameTag);
});
this.scriptService.editScript(this.newData, this.defaultName, fullName, this.description, listOfTags, this.editedId).subscribe(
() => {
this.router.navigateByUrl('scriptmanagement');
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
}
onItemSelect(item: any) {
}
onSelectAll(items: any) {
this.selectedTagList = items;
}
onItemDeselect(item: any){
}
onDeselectAll(items: any){
this.selectedTagList = [];
}
}

@ -0,0 +1,112 @@
<div class="max-w-full rounded-2xl overflow-hidden shadow-lg bg-white">
<div class="m-2 justify-items-center grid grid-cols-1 sm:grid-cols-12 gap-4 ">
<div class="sm:col-span-3 my-auto">
<button class="flex btn-grad-purple-blue m-2" (click)="getAllScriptsWithTags(); getAllScripts()">Rafraichir</button>
</div>
<div class="sm:col-span-6 my-auto text-2xl font-bold">
Gestionnaire de Scripts
</div>
<div class="sm:col-span-3 my-auto md:flex">
<a routerLink="/tagsmanagement" class="flex btn-grad-red-yellow font-semibold m-2" >Gérer les Tags</a>
<button (click)="addScript()" class="flex btn-grad-red-yellow font-semibold m-2" >Ajouter un Script</button>
</div>
</div>
</div>
<div class="max-w-full rounded-2xl overflow-hidden shadow-lg bg-white my-4 p-4">
<div class="grid grid-cols-1 md:grid-cols-3 md:gap-4">
<div class="col-span-2 rounded-lg overflow-hidden shadow-lg bg-slate-100 border-solid border-orange-400 border-2 p-4">
<div class="flex space-x-2">
<input [(ngModel)]="filterScriptName" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" (ngModelChange)="sortWithFilter()" placeholder="Rechercher par nom ou description...">
</div>
<div>
<ng-multiselect-dropdown
[placeholder]="'Selectionner les Tags'"
[settings]="dropdownSettings"
[data]="dropdownTagList"
[(ngModel)]="selectedTagList"
(onSelect)="sortWithFilter()"
(onSelectAll)="onSelectAll($event)"
(onDeSelect)="sortWithFilter()"
(onDeSelectAll)="onDeselectAll()"
>
</ng-multiselect-dropdown>
</div>
<div class="overflow-y-auto h-96">
<div class="grid grid-cols-1 md:grid-cols-2">
<div *ngFor="let script of allScripts, index as i" class="grid grid-cols-2 rounded-2xl overflow-hidden shadow-lg bg-gray-300 p-4 m-2 " (click)="displayText(script)">
<div class="flex justify-start">
<div class="hidden xl:block font-bold font-mono mr-2 text-lg rounded-full overflow-hidden shadow-lg bg-green-300 p-2">
{{script.extension}}
</div>
<div class="my-auto font-bold ">
{{script.name}}
</div>
</div>
<div class="my-auto flex justify-end">
<mat-icon (click)="editScript(script)" class="text-orange-400 ml-2 hover:cursor-pointer" svgIcon="edit"></mat-icon>
<mat-icon (click)="deleteSpecifiedScript(script.name)" class="text-red-600 mx-2 hover:cursor-pointer" svgIcon="trash"></mat-icon>
</div>
</div>
</div>
</div>
</div>
<div class="mt-4 md:mt-0">
<cdk-accordion>
<cdk-accordion-item >
<div class="rounded-lg bg-slate-300 shadow-lg p-2">
<div class="flex justify-between hover:cursor-pointer" (click)="descriptionDisplay = !descriptionDisplay">
<div class="text-xl font-bold pb-2">Description :</div>
<span class="example-accordion-item-description" class="font-semibold">
Cliquer pour {{ descriptionDisplay ? 'fermer' : 'ouvrir' }}
</span>
</div>
<div
role="region"
[style.display]="descriptionDisplay ? '' : 'none'"
class="rounded-lg bg-white p-2 shadow-md"
*ngIf="descText">
{{ descText }}
</div>
</div>
</cdk-accordion-item>
<cdk-accordion-item>
<div class="rounded-lg bg-slate-300 p-2 mt-2">
<div (click)="contentDisplay = !contentDisplay" class="flex justify-between hover:cursor-pointer">
<div class="text-xl font-bold pb-2">Contenu du Script :</div>
<span class="example-accordion-item-description" class="font-semibold">
Cliquer pour {{ contentDisplay ? 'fermer' : 'ouvrir' }}
</span>
</div>
<div
role="region"
[style.display]="contentDisplay ? '' : 'none'">
<textarea rows="20" class="block p-2.5 w-full text-sm rounded-lg border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-orange-400 focus:border-orange-400">
{{textTo}}</textarea>
</div>
</div>
</cdk-accordion-item>
<cdk-accordion-item>
<div class="rounded-lg bg-slate-300 p-2 mt-2">
<div (click)="tagsDisplay = !tagsDisplay" class="flex justify-between hover:cursor-pointer">
<div class="text-xl font-bold pb-2">Tags associés :</div>
<span class="example-accordion-item-description" class="font-semibold">
Cliquer pour {{ tagsDisplay ? 'fermer' : 'ouvrir' }}
</span>
</div>
<div
role="region"
[style.display]="tagsDisplay ? '' : 'none'">
<div class="grid grid-cols-2 xl:grid-cols-4 text-center">
<div *ngFor="let tagString of tagsListOfSpecScript" class="rounded-2xl overflow-hidden shadow-lg bg-green-300 p-4 m-2 font-bold">
{{tagString}}
</div>
</div>
</div>
</div>
</cdk-accordion-item>
</cdk-accordion>
</div>
</div>
</div>

@ -0,0 +1,192 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ScriptService } from '../../service/script.service';
import { Script } from '../../model/script';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { Router, NavigationExtras } from '@angular/router';
import { TagsService } from '../../service/tags.service';
import { Tag } from '../../model/tag';
import { LinkScriptTag } from '../../model/link-script-tag';
@Component({
selector: 'app-script-management',
templateUrl: './script-management.component.html',
styleUrls: ['./script-management.component.scss']
})
export class ScriptManagementComponent implements OnInit {
textTo: String | undefined;
descText: String | undefined;
tagsListOfSpecScript: String[] | undefined;
allScripts: Script[] = [];
memoryScripts: Script[] = [];
allLinksScriptTag: LinkScriptTag[]= [];
filterScriptName: string = "";
dropdownTagList: Tag[] = [];
selectedTagList: Tag[] = [];
dropdownSettings = {};
descriptionDisplay: boolean = true;
tagsDisplay: boolean = true;
contentDisplay: boolean = false;
constructor(
private router: Router,
private tagService: TagsService,
private scriptService: ScriptService,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer
){
this.matIconRegistry.addSvgIcon('edit', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/edit.svg')),
this.matIconRegistry.addSvgIcon('trash', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/trash.svg')),
this.matIconRegistry.addSvgIcon('refresh', this.domSanitizer.bypassSecurityTrustResourceUrl('../../assets/svg/arrows.svg'))
}
ngOnInit(): void{
this.getAllScriptsWithTags();
this.getAllScripts();
this.retrieveTags();
this.dropdownSettings = {
singleSelection: false,
idField: "tagId",
textField: "nameTag",
selectAllText: "Tout Sélectionner",
unSelectAllText: "Déselectionner tout",
itemsShowLimit: 3,
allowSearchFilter: true
};
}
public displayText(script: Script){
if(this.allScripts){
this.textTo = script.data;
const tempDescScript = this.allLinksScriptTag.filter(link => link.scriptName === script.name);
this.descText = tempDescScript[0].description;
this.tagsListOfSpecScript = tempDescScript[0].tags;
}
}
public retrieveTags(){
this.tagService.getAllTags().subscribe(
(response: Tag[]) => {
this.dropdownTagList = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
onSelectAll(items: any) {
this.selectedTagList = items;
this.sortWithFilter();
}
onDeselectAll(){
this.selectedTagList = [];
this.allScripts = this.memoryScripts;
}
public sortWithFilter(){
let tempLinksScriptsWithTags: LinkScriptTag[] = [];
if(this.selectedTagList.length !==0){
this.allLinksScriptTag.forEach((one, i ) => {
const oneData = one.tags;
if(this.selectedTagList.every(sel => oneData.includes(sel.nameTag))){
tempLinksScriptsWithTags.push(one);
}
})
} else {
tempLinksScriptsWithTags = this.allLinksScriptTag;
}
if(!this.allScripts || !this.filterScriptName){
this.allScripts = this.memoryScripts.filter(scr => tempLinksScriptsWithTags.some(temp => temp.scriptName === scr.name))
}
if(this.filterScriptName){
const tempFindDesc = tempLinksScriptsWithTags.filter(lnk => lnk.description.toLowerCase().includes(this.filterScriptName))
this.allScripts = this.memoryScripts.filter(item => item.name.toLowerCase().indexOf(this.filterScriptName) !== -1 && tempLinksScriptsWithTags.some(tm => tm.scriptName === item.name) || tempFindDesc.some(tmp => tmp.scriptName === item.name));
}
}
public editScript(script: Script){
let linkId;
let editedTagList: Tag[] = [];
this.allLinksScriptTag.forEach((linkTag, i) => {
if(linkTag.scriptName == script.name){
linkId = linkTag.id;
editedTagList = this.dropdownTagList.filter(dp => linkTag.tags.some(lt => lt === dp.nameTag));
}
});
const tempDescScript = this.allLinksScriptTag.filter(link => link.scriptName === script.name);
const navigationExtras: NavigationExtras = {
state: {
name: script.name,
extension: script.extension,
data: script.data,
linkId: linkId,
tags: editedTagList,
description: tempDescScript[0].description,
}
};
this.router.navigate(['editscript'], navigationExtras);
}
public addScript(){
let remId;
if(this.allLinksScriptTag.length > 0){
remId = this.allLinksScriptTag[this.allLinksScriptTag.length - 1].id + 1;
} else {
remId = 1;
}
const navigationExtras: NavigationExtras = {
state: {
nextId: remId
}
};
this.router.navigate(['addscript'], navigationExtras);
}
public getAllScripts(){
this.scriptService.retreiveScripts().subscribe(
(response : Script[]) => {
this.allScripts = response;
this.memoryScripts = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public getAllScriptsWithTags(){
this.scriptService.getAllScriptsWithTags().subscribe(
(response : LinkScriptTag[]) => {
this.allLinksScriptTag = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public deleteSpecifiedScript(name: String){
if(window.confirm('Êtes-vous sûr de vouloir supprimer ce Script ?')){
this.scriptService.deleteScript(name).subscribe(
() => {
this.getAllScripts();
this.textTo = undefined;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
}

@ -0,0 +1,58 @@
<div class="text-center rounded-lg overflow-hidden shadow-lg bg-white p-4 my-4">
<div class="text-2xl font-bold">Gestion des Tags</div>
<div class="grid grid-cols-3 gap-4 mt-2">
<button class="btn-grad-teal-lime mx-2" (click)="addPress = !addPress">Ajouter un Tag</button>
<input [(ngModel)]="filterTag" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Rechercher...">
<button class="btn-grad-pink-orange mx-2" (click)="deleteAllTags()" disabled>Supprimer tous les Tags</button>
</div>
<div *ngIf="addPress">
<div class="bg-orange-400 w-full h-2 overflow-hidden my-4 rounded-lg"></div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div class="text-lg font-semibold">Nom du Tag : </div>
<input [(ngModel)]="nameOfTag" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Taper le nom ici...">
</div>
<div>
<div class="text-lg font-semibold">Description du Tag : </div>
<input [(ngModel)]="descOfTag" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Taper la description ici...">
</div>
</div>
<button class="btn-grad-green-blue mx-2 mt-4" (click)="addTag()">Valider</button>
</div>
<div class="bg-orange-400 w-full h-2 overflow-hidden my-4 rounded-lg"></div>
<div *ngIf="modifyPress">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div class="text-lg font-semibold">Nom du Tag : </div>
<div>{{ previousName }}</div>
<div class="text-lg font-semibold">Nouveau nom : </div>
<input [(ngModel)]="modifyName" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Taper le nom ici...">
</div>
<div>
<div class="text-lg font-semibold">Description du Tag : </div>
<div>{{ previousDesc }}</div>
<div class="text-lg font-semibold">Nouvelle description : </div>
<input [(ngModel)]="modifyDesc" type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" placeholder="Taper la description ici...">
</div>
</div>
<button class="btn-grad-green-blue mx-2 my-4" (click)="modifyTag()">Valider</button>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
<div *ngFor="let tag of allTags | filter: filterTag" class="rounded-2xl overflow-hidden shadow-lg bg-slate-800 p-2">
<div class="text-white font-bold">Nom du Tag : {{ tag.nameTag }}</div>
<div class="text-white">{{ tag.descriptionTag }}</div>
<div class="flex justify-between my-2">
<button class="btn-grad-purple-blue mx-2" (click)="pressModifyBtn(tag.tagId, tag.nameTag, tag.descriptionTag)">Modifier</button>
<button class="btn-grad-pink-orange mx-2" (click)="deleleTag(tag)">Supprimer</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,115 @@
import { Component, OnInit } from '@angular/core';
import { TagsService } from '../../service/tags.service';
import { HttpErrorResponse } from '@angular/common/http';
import { Tag } from '../../model/tag';
@Component({
selector: 'app-tags-management',
templateUrl: './tags-management.component.html',
styleUrls: ['./tags-management.component.scss']
})
export class TagsManagementComponent implements OnInit{
allTags: Tag[]= [];
filterTag: string = "";
nameOfTag: string | undefined;
descOfTag: string | undefined;
addPress: boolean = false;
modifyPress: boolean = false;
previousId: number | undefined
previousName: string | undefined;
previousDesc: string | undefined;
modifyName: string | undefined;
modifyDesc: string | undefined;
constructor(private tagService: TagsService){}
ngOnInit(): void{
this.retrieveTags();
}
public retrieveTags(){
this.tagService.getAllTags().subscribe(
(response: Tag[]) => {
this.allTags = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
public addTag(){
if(this.nameOfTag && this.descOfTag){
let tag: Tag = {
tagId: this.allTags[this.allTags.length - 1].tagId + 1,
nameTag: this.nameOfTag,
descriptionTag: this.descOfTag
}
this.tagService.addTag(tag).subscribe(
() => {
this.addPress = false;
this.nameOfTag =undefined;
this.descOfTag = undefined;
this.retrieveTags();
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
}
public modifyTag(){
if(this.previousName && this.previousDesc && this.previousId && this.modifyName && this.modifyDesc){
let modifyTag: Tag = {
tagId: this.previousId,
nameTag: this.modifyName.toString(),
descriptionTag: this.modifyDesc.toString()
}
this.tagService.updateTag(this.previousName.toString(), modifyTag).subscribe(
() => {
this.modifyPress = false;
this.previousId = undefined;
this.previousName = undefined;
this.previousDesc = undefined;
this.modifyName = undefined;
this.modifyDesc = undefined;
this.retrieveTags();
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
}
public deleleTag(tag: Tag){
this.tagService.deleteTag(tag.nameTag).subscribe(
() => {
this.retrieveTags();
},
(error: HttpErrorResponse) => {
alert(error.message)
});
}
public deleteAllTags(){
if(window.confirm('Êtes-vous sûr de vouloir supprimer tous les tags ?')){
this.tagService.deleteAllTags().subscribe(
() => {
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public pressModifyBtn(id: number, name: string, desc: string){
this.previousId = id;
this.previousName = name;
this.previousDesc = desc;
this.modifyName = name;
this.modifyDesc = desc;
this.modifyPress = !this.modifyPress;
}
}

@ -0,0 +1,60 @@
<nav class="sticky top-0 left-0 right-0 z-50 bg-slate-900 hover:border-solid hover:border-b-2 hover:border-white border-b-2 border-slate-900 hover:ease-in-out duration-300">
<div class="mx-auto max-w-7xl px-2 md:px-6 lg:px-8 relative flex h-16 items-center justify-between">
<div class="absolute inset-y-0 left-0 flex items-center md:hidden">
<!-- Mobile menu button-->
<button type="button" class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false">
<svg (click)="openMobileMenu()" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
</div>
<div class="flex flex-1 items-center justify-center md:items-stretch md:justify-start">
<div class="flex flex-shrink-0 items-center">
<img routerLink="" class="block h-8 w-auto lg:hidden hover:cursor-pointer" src="../../assets/images/logo-apsidetop-blanc.png" alt="Your Company">
<img routerLink="" class="hidden h-8 w-auto lg:block hover:cursor-pointer" src="../../assets/images/logo-apsidetop-blanc.png" alt="Your Company">
</div>
<div class="hidden md:ml-6 md:flex space-x-4" id="extend-menu">
<a routerLinkActive="active" routerLink="request" class="btn-gray-simple">Recherche</a>
<a routerLinkActive="active" routerLink="joins" class="btn-gray-simple">Jointures</a>
<a routerLinkActive="active" routerLink="createjoins" class="btn-gray-simple">Création</a>
<a routerLinkActive="active" routerLink="scriptmanagement" class="btn-gray-simple">Gestionnaire</a>
<button (click)="applyResetDatabase()" class="btn-orange-simple">MAJ BDD</button>
<mat-spinner *ngIf="isLoading" diameter="30"></mat-spinner>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 md:static md:inset-auto md:ml-6 md:pr-0">
<!-- Profile dropdown -->
<div class="relative ml-3">
<button type="button" class="flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
<img class="h-8 w-8 rounded-full border-solid border-2 border-white" src="../../assets/images/userpng.png" alt="">
</button>
</div>
</div>
</div>
<!-- Mobile menu, show/hide based on menu state. -->
<div *ngIf="mobileOpen" class="md:hidden" id="mobile-menu">
<div class="space-y-1 px-2 pt-2 pb-3">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<a routerLink="request" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Recherche</a>
<a routerLink="joins" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Jointures</a>
<a routerLink="createjoins" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Création</a>
<a routerLink="scriptmanagement" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Gestionnaire</a>
<button (click)="applyResetDatabase()" class="ml-2 text-white bg-orange-400 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium hover:animate-pulse">MAJ BDD</button>
<mat-spinner *ngIf="isLoading" diameter="30" class="ml-2 mt-2"></mat-spinner>
</div>
</div>
</nav>

@ -0,0 +1,4 @@
.active {
background-color: rgb(59 130 246);
color: white;
}

@ -0,0 +1,52 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import { ResetDataService } from '../../service/reset_data.service';
import { Router, RouterModule } from '@angular/router';
import { AppRoutingModule } from '../../app-routing.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent {
public mobileOpen: boolean = false;
public isLoading: boolean = false;
constructor(private resetDataService: ResetDataService, private router: Router){}
public openMobileMenu(){
this.mobileOpen = !this.mobileOpen;
}
public applyResetDatabase():void {
if(window.confirm('Êtes-vous sûr de vouloir recharger le BDD ? Cela peut prendre du temps !')){
this.isLoading = true;
this.resetDataService.resetDatabase().subscribe(
() => {
this.router.navigateByUrl('');
this.isLoading = false;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
}
public testreset(): void {
this.isLoading = true;
this.resetDataService.testres().subscribe(
() => {
this.router.navigateByUrl('');
this.isLoading = false;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
)
}
}

@ -0,0 +1,5 @@
export interface Alias {
nameSchema: String,
nameTable: String,
nameAlias: String,
}

@ -0,0 +1,5 @@
export interface ColumnData {
operator: String;
aliasColumn: String;
specColumn: String;
}

@ -0,0 +1,7 @@
export interface Condition {
operator: String;
alias: String;
column: String;
condition: String;
value: String;
}

@ -0,0 +1,7 @@
export interface Employee {
id: number;
firstName: string;
lastName: string;
mail: string;
password: string;
}

@ -0,0 +1,7 @@
export interface InfoColumn {
id: number;
nameColumn: string;
dataType: string;
lengthColumn: number;
columnText: string;
}

@ -0,0 +1,6 @@
export interface InfoTable {
id: number;
nameTable: string;
nameSchema: string;
tableText: string;
}

@ -0,0 +1,9 @@
import { ColumnData } from './column-data';
export interface Join {
joinSpec: String;
specificSchema: String;
specificTable: String;
baseAlias: String;
columns: ColumnData[];
aliasSpecificTable: String;
}

@ -0,0 +1,6 @@
export interface LinkScriptTag {
id: number;
scriptName: String;
description: String;
tags: String[];
}

@ -0,0 +1,5 @@
export interface Script {
extension: String,
name: String,
data: String
}

@ -0,0 +1,5 @@
export interface Tag {
tagId: number;
nameTag: string;
descriptionTag: string;
}

@ -0,0 +1,96 @@
<button class="flex btn-white-simple my-2" (click)="refreshEverything()">Réinitialiser</button>
<div class="max-w-full rounded-2xl overflow-hidden shadow-lg bg-white">
<mat-vertical-stepper class="text-white" [linear]="true">
<mat-step label="Choisissez un Schéma">
<div class="my-2">
<label class="text-black">Schémas : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterSchema" placeholder="Rechercher...">
<select [(ngModel)]="schemaName" (change)="useTablesWithSchemaName()" *ngIf="!schemasByTable" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of allSchema | filter : filterSchema" [ngValue]="schema">{{ schema }}</option>
</select>
<select [(ngModel)]="schemaData" (change)="useTablesWithSchemaName()" *ngIf="schemasByTable" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let schema of schemasByTable | filter : filterSchema" [ngValue]="schema">{{ schema.nameSchema }}</option>
</select>
<button class="btn-white-simple" matStepperNext>Continuer</button>
</div>
</mat-step>
<mat-step label="Choisissez une Table">
<div class="my-2">
<label class="text-black">Tables : </label>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterTable" (ngModelChange)="sortCorrectly()" placeholder="Rechercher..." >
<select [(ngModel)]="tableData" (change)="useSchemaWithTableName()" *ngIf="!tablesBySchema" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of infoTable | filter : filterTable" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
<select [(ngModel)]="tableData" (change)="useSchemaWithTableName()" *ngIf="tablesBySchema" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let table of tablesBySchema | filter : filterTable" id="{{ table.id }}" [ngValue]="table">{{ table.nameTable }}</option>
</select>
<button class="btn-white-simple mr-2 mb-2" matStepperPrevious>Retour</button>
<button class="btn-white-simple" matStepperNext>Continuer</button>
</div>
</mat-step>
<mat-step label="(Optionnel) Choisissez une colonne">
<div class="my-2">
<label class="text-black mb-4">Colonnes : </label>
<button class="flex btn-white-simple my-2" (click)="resetColumnsInformations()">Reset</button>
<input type="text" class="block w-full my-2 pl-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-slate-800 focus:border-slate-800" [(ngModel)]="filterColumn" placeholder="Rechercher...">
<select (change)="displayColumnsInformations($event)" size="5" class="mb-4 bg-gray-50 text-sm text-slate-800 rounded-lg focus:ring-slate-800 focus:border-slate-800 block w-full p-2.5 bg-none">
<option *ngFor="let column of infoColumn | filter: filterColumn" id="{{ column.id }}" value="{{ column.id }}">{{ column.nameColumn }} ({{ column.columnText }})</option>
</select>
<button class="btn-white-simple mr-2 mb-2" matStepperPrevious>Retour</button>
</div>
</mat-step>
</mat-vertical-stepper>
</div>
<div class="my-4 grid grid-cols-1 sm:grid-cols-3 gap-5">
<!--Card-->
<div *ngIf="currentTable" class="sm:col-start-2 rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 py-4 grid justify-items-center">
<div class="font-bold text-xl">Table : {{ currentTable.nameTable }}</div>
<div class="font-bold text-xl">Schéma : {{ selectedSchema }}</div>
<div class="bg-orange-400 w-full h-2 rounded-lg overflow-hidden my-2"></div>
<p class="text-base">{{ currentTable.tableText }}</p>
</div>
</div>
</div>
<div *ngIf="!displayColumns" class="my-4 grid sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
<!--Card-->
<div *ngFor="let column of infoColumn" class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">Colonne : {{ column.nameColumn }}</div>
<p class="text-base">Data Type : {{ column.dataType }}</p>
<p class="text-base">Taille : {{ column.lengthColumn }}</p>
<p class="text-base">Description : {{ column.columnText }}</p>
</div>
</div>
</div>
<div *ngIf="displayColumns" class="my-4 grid sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
<!--Card-->
<div class="rounded-lg overflow-hidden shadow-lg bg-white">
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">Colonne : {{ displayColumns.nameColumn }}</div>
<p class="text-base">Data Type : {{ displayColumns.dataType }}</p>
<p class="text-base">Taille : {{ displayColumns.lengthColumn }}</p>
<p class="text-base">Description : {{ displayColumns.columnText }}</p>
</div>
</div>
</div>

@ -0,0 +1,171 @@
import { HttpErrorResponse } from '@angular/common/http';
import {FormBuilder, Validators} from '@angular/forms';
import { Component, Input, OnInit } from '@angular/core';
import { Employee } from '../../model/employee';
import { EmployeeService } from '../../service/employee.service';
import { InfoTable } from '../../model/info-table';
import { InfoTableService } from '../../service/info-table.service';
import { InfoColumn } from '../../model/info-column';
import { InfoColumnService } from '../../service/info-column.service';
import { MatStepperModule } from '@angular/material/stepper';
@Component({
selector: 'app-request-page',
templateUrl: './request-page.component.html',
styleUrls: ['./request-page.component.scss']
})
export class RequestPageComponent implements OnInit {
public allSchema: String[] | undefined;
public infoTable: InfoTable[] | undefined;
public infoColumn: InfoColumn[] | undefined;
public tablesBySchema: InfoTable[] | undefined;
public schemasByTable: InfoTable[] | undefined;
public displayColumns: InfoColumn | undefined;
public currentTable: InfoTable | undefined;
public selectedSchema: String | undefined;
filterSchema: string = "";
filterTable: string = "";
filterColumn: string = "";
tableData: InfoTable | undefined;
schemaData: any;
schemaName: String | undefined;
firstFormGroup = this._formBuilder.group({
firstCtrl: ['', Validators.required],
});
secondFormGroup = this._formBuilder.group({
secondCtrl: ['', Validators.required],
});
constructor(
private _formBuilder: FormBuilder ,
private infoColumnService: InfoColumnService,
private infoTableService: InfoTableService
){}
ngOnInit(): void {;
this.getSchemas();
//this.getTables();
}
public getSchemas():void {
this.infoTableService.getSchemas().subscribe(
(response : String[]) => {
this.allSchema = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public getTables():void {
this.infoTableService.getAllTables().subscribe(
(response : InfoTable[]) => {
this.infoTable = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public sortCorrectly(){
if(this.filterTable.length > 2){
if(this.schemaName){
this.infoTableService.getTablesWithFilterAndSchema(this.filterTable, this.schemaName).subscribe(
(response: InfoTable[]) => {
this.tablesBySchema = response;
});
} else {
this.infoTableService.getTablesWithFilter(this.filterTable).subscribe(
(response: InfoTable[]) => {
this.infoTable = response;
});
}
}
}
public divideColumns(nameSch: String, tempsTab: InfoTable):void {
this.infoColumnService.getSelectedColumns(nameSch, tempsTab.nameTable).subscribe(
(response : InfoColumn[]) => {
this.infoColumn = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
this.displayColumns = undefined;
this.infoTableService.getTable(tempsTab.id).subscribe(
(response : InfoTable) => {
this.currentTable = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public displayColumnsInformations (event: any){
this.infoColumnService.getColumn(event.target.value).subscribe(
(response : InfoColumn) => {
this.displayColumns = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
}
public useTablesWithSchemaName (){
if(this.schemaName){
this.selectedSchema = this.schemaName;
} else {
this.selectedSchema = this.schemaData.nameSchema;
}
if(this.selectedSchema){
if(this.tableData){
this.divideColumns(this.selectedSchema, this.tableData);
}
}
}
public useSchemaWithTableName (){
if(this.tableData){
this.infoTableService.getSchemasByTable(this.tableData.nameTable).subscribe(
(response : InfoTable[]) => {
this.schemasByTable = response;
},
(error: HttpErrorResponse) => {
alert(error.message)
}
);
if(this.selectedSchema){
this.divideColumns(this.selectedSchema, this.tableData);
}
this.schemaName = undefined;
}
}
public resetColumnsInformations(){
this.displayColumns = undefined;
}
public refreshEverything(){
this.tablesBySchema = undefined;
this.schemasByTable = undefined;
this.selectedSchema = undefined;
this.displayColumns = undefined;
this.infoColumn = undefined;
this.currentTable = undefined;
this.tableData = undefined;
this.filterTable = "";
this.infoTable = undefined;
this.schemaName = undefined;
}
}

@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { InfoColumn } from '../model/info-column';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class InfoColumnService {
constructor(private http: HttpClient) { }
public getAllColumns(): Observable<InfoColumn[]>{
return this.http.get<InfoColumn[]>(`/api/columns/all`);
}
public getColumn(columnId: Number): Observable<InfoColumn>{
return this.http.get<InfoColumn>(`/api/column/${columnId}`);
}
public getSelectedColumns(schema: String, table: String): Observable<InfoColumn[]>{
return this.http.get<InfoColumn[]>(`/api/columns/${schema}/${table}`);
}
public getColumnsForJoin(firstSchema: String, secondSchema: String, firstTable: String, secondTable: String): Observable<InfoColumn[]>{
return this.http.get<InfoColumn[]>(`/api/columns/${firstSchema}/${secondSchema}/${firstTable}/${secondTable}`);
}
public getColumnsForJoinTwo(tables: string[], schemas: string[]): Observable<InfoColumn[]>{
const paramsGetJoins = new HttpParams()
.set('tables', tables.toString())
.set('schemas', schemas.toString());
return this.http.get<InfoColumn[]>(`/api/columns/joins`, {params: paramsGetJoins});
}
}

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { InfoTable } from '../model/info-table';
@Injectable({
providedIn: 'root'
})
export class InfoTableService {
constructor(private http: HttpClient) { }
public getAllTables(): Observable<InfoTable[]>{
return this.http.get<InfoTable[]>(`/api/tables/all`);
}
public getTable(tableId: Number): Observable<InfoTable>{
return this.http.get<InfoTable>(`/api/table/${tableId}`);
}
public getSchemas(): Observable<String[]>{
return this.http.get<String[]>(`/api/schemas/all`);
}
public getTablesBySchema(nameSchema : String): Observable<InfoTable[]>{
return this.http.get<InfoTable[]>(`/api/tables/${nameSchema}`);
}
public getSchemasByTable(nameTable : String): Observable<InfoTable[]>{
return this.http.get<InfoTable[]>(`/api/schemas/${nameTable}`);
}
public getTablesWithFilter(filter : String): Observable<InfoTable[]>{
return this.http.get<InfoTable[]>(`/api/tables/filter/${filter}`);
}
public getTablesWithFilterAndSchema(filter : String, schema: String): Observable<InfoTable[]>{
return this.http.get<InfoTable[]>(`/api/tables/filter/${schema}/${filter}`);
}
}

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ResetDataService {
constructor(private http: HttpClient) { }
public resetDatabase(){
return this.http.post<void>(`/api/reset`, null);
}
public testres(){
return this.http.post<void>(`/api/testres`, null);
}
}

@ -0,0 +1,44 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Script } from '../model/script';
import { LinkScriptTag } from '../model/link-script-tag';
@Injectable({
providedIn: 'root'
})
export class ScriptService {
constructor(private http: HttpClient) { }
public retreiveScripts(): Observable<Script[]>{
return this.http.get<Script[]>(`/api/scripts`);
}
public getAllScriptsWithTags(): Observable<LinkScriptTag[]>{
return this.http.get<LinkScriptTag[]>(`/api/scripts/link`);
}
public deleteScript(name: String){
return this.http.delete<void>(`/api/script/delete/${name}`);
}
public addOneScript(script: Script, description: String, tagList: String[], id: number){
const paramsAddScript = new HttpParams()
.set('name', script.name.toString())
.set('desc', description.toString())
.set('tagList', tagList.join(', '))
.set('linkid', id);
return this.http.post<void>(`/api/script/add`, script.data, {params: paramsAddScript});
}
public editScript(content: String, defaultName: String, newName: String, description: String, tagList: String[], id: number){
const paramsAddScript = new HttpParams()
.set('defaultname', defaultName.toString())
.set('newname', newName.toString())
.set('desc', description.toString())
.set('tagList', tagList.join(', '))
.set('linkid', id);
return this.http.post<void>(`/api/script/edit`, content, {params: paramsAddScript});
}
}

@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Tag } from '../model/tag';
@Injectable({
providedIn: 'root'
})
export class TagsService {
constructor(private http: HttpClient) { }
public addTag(tag: Tag){
return this.http.post<void>(`/api/tag/add`, tag);
}
public updateTag(prevName: string, tag: Tag){
return this.http.put<void>(`/api/tag/update/${prevName}`, tag);
}
public getTag(tagId: Number): Observable<Tag>{
return this.http.get<Tag>(`/api/tag/${tagId}`);
}
public getAllTags(): Observable<Tag[]>{
return this.http.get<Tag[]>(`/api/tags/all`);
}
public deleteTag(tagName: String){
return this.http.delete<void>(`/api/tag/delete/${tagName}`);
}
public deleteAllTags(){
return this.http.delete<void>(`/api/tags/deleteAll`);
}
}

@ -0,0 +1,51 @@
@layer components {
.btn-white-simple {
@apply bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow;
}
.btn-gray-simple {
@apply text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium;
}
.btn-orange-simple {
@apply text-white bg-orange-400 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium hover:animate-pulse;
}
.btn-grad-red-rose {
@apply text-gray-900 text-lg bg-gradient-to-br from-rose-600 to-red-400 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-red-200 font-medium rounded-full px-5 py-2.5 text-center;
}
.btn-grad-green-teal {
@apply text-white text-lg bg-gradient-to-br from-teal-400 to-green-500 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-green-200 font-medium rounded-full px-5 py-2.5 text-center;
}
.btn-grad-pink-orange {
@apply text-white bg-gradient-to-br from-pink-500 to-orange-400 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-pink-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center;
}
.btn-grad-red-yellow {
@apply text-gray-900 bg-gradient-to-r from-red-200 via-red-300 to-yellow-200 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-red-100 font-medium rounded-lg text-sm px-5 py-2.5 text-center;
}
.btn-grad-green-blue {
@apply text-white bg-gradient-to-br from-green-400 to-blue-600 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-green-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center;
}
.btn-grad-cyon-blue {
@apply text-white bg-gradient-to-r from-cyan-500 to-blue-500 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-cyan-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center;
}
.btn-grad-purple-blue {
@apply text-white bg-gradient-to-br from-purple-600 to-blue-500 hover:bg-gradient-to-bl focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium py-2.5 px-5 rounded-lg text-sm text-center;
}
.btn-grad-teal-lime {
@apply text-black bg-gradient-to-r from-teal-200 to-lime-200 hover:bg-gradient-to-l hover:from-teal-200 hover:to-lime-200 focus:ring-4 focus:outline-none focus:ring-lime-200 font-medium py-2.5 px-5 rounded-lg text-center;
}
.btn-grad-purple-pink {
@apply text-white bg-gradient-to-r from-purple-500 to-pink-500 hover:bg-gradient-to-l focus:ring-4 focus:outline-none focus:ring-purple-200 font-semibold py-2 px-4 rounded text-center;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8c62.5-62.5 163.8-62.5 226.3 0L386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H463.5c0 0 0 0 0 0h.4c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2c-4 4-6.7 8.8-8.1 14c-.3 1.2-.6 2.5-.8 3.8c-.3 1.7-.4 3.4-.4 5.1V448c0 17.7 14.3 32 32 32s32-14.3 32-32V396.9l17.6 17.5 0 0c87.5 87.4 229.3 87.4 316.7 0c24.4-24.4 42.1-53.1 52.9-83.7c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.5 62.5-163.8 62.5-226.3 0l-.1-.1L125.6 352H176c17.7 0 32-14.3 32-32s-14.3-32-32-32H48.4c-1.6 0-3.2 .1-4.8 .3s-3.1 .5-4.6 1z"/></svg>

After

Width:  |  Height:  |  Size: 996 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M272 0H396.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9V336c0 26.5-21.5 48-48 48H272c-26.5 0-48-21.5-48-48V48c0-26.5 21.5-48 48-48zM48 128H192v64H64V448H256V416h64v48c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48z"/></svg>

After

Width:  |  Height:  |  Size: 503 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L362.3 51.7l97.9 97.9 30.1-30.1c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L437.7 172.3 339.7 74.3 172.4 241.7zM96 64C43 64 0 107 0 160V416c0 53 43 96 96 96H352c53 0 96-43 96-96V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H96z"/></svg>

After

Width:  |  Height:  |  Size: 749 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>

After

Width:  |  Height:  |  Size: 502 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="300" width="300" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 109.3V352c0 17.7-14.3 32-32 32s-32-14.3-32-32V109.3l-73.4 73.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l128-128c12.5-12.5 32.8-12.5 45.3 0l128 128c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L288 109.3zM64 352H192c0 35.3 28.7 64 64 64s64-28.7 64-64H448c35.3 0 64 28.7 64 64v32c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V416c0-35.3 28.7-64 64-64zM432 456a24 24 0 1 0 0-48 24 24 0 1 0 0 48z"/></svg>

After

Width:  |  Height:  |  Size: 675 B

@ -0,0 +1,3 @@
export const environment = {
production: false,
};

@ -0,0 +1,3 @@
export const environment = {
production: false,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularTest</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="bg-slate-900">
<app-root ></app-root>
</body>
</html>

@ -0,0 +1,7 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

@ -0,0 +1,13 @@
/* You can add global styles to this file, and also import other style files */
/* More informations on https://tailwindcss.com/ */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import 'assets/button.scss';
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }

@ -0,0 +1,17 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: '',
purge: {
content: [
'./src/**/*.{html,ts}',
]
},
darkMode: 'class', // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [require('@tailwindcss/forms'),require('@tailwindcss/typography')],
};

@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}

@ -0,0 +1,33 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
Loading…
Cancel
Save