Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
526f4d049b | 2 years ago |
@ -1,36 +1,10 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
name:
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: node:20-alpine3.19
|
||||
image: node:20-alpine3.18
|
||||
commands:
|
||||
- npm install
|
||||
- npm run build
|
||||
|
||||
- name: test-chrome
|
||||
image: timbru31/node-chrome:20-slim
|
||||
commands:
|
||||
- npm run test -- --browsers=ChromeHeadlessCI --watch=false
|
||||
depends_on:
|
||||
- build
|
||||
|
||||
- name: sonar
|
||||
image: sonarsource/sonar-scanner-cli:5
|
||||
commands:
|
||||
- sonar-scanner -Dsonar.projectKey="$PROJECT_KEY" -Dsonar.login="$SONAR_TOKEN" -Dsonar.host.url=https://codefirst.iut.uca.fr/sonar
|
||||
environment:
|
||||
PROJECT_KEY:
|
||||
from_secret: SONAR_PROJECT_KEY
|
||||
SONAR_TOKEN:
|
||||
from_secret: SONAR_TOKEN
|
||||
depends_on:
|
||||
- build
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
- ci/*
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
- npm install -g @angular/cli
|
||||
- ng build
|
@ -1,41 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["projects/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended",
|
||||
"plugin:@angular-eslint/template/accessibility"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "es5",
|
||||
"bracketSameLine": true,
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto"
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-firefox-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/sandkasten'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
restartOnFileChange: true,
|
||||
customLaunchers: {
|
||||
ChromeHeadlessCI: {
|
||||
base: 'ChromeHeadless',
|
||||
flags: ['--no-sandbox']
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { EditorComponent } from './editor/editor.component';
|
||||
import { LandingPageComponent } from './landing-page/landing-page.component';
|
||||
import { OutputComponent } from './output/output.component';
|
||||
import {DocumentationComponent} from "./documentation/documentation.component";
|
||||
import { ContactComponent } from './contact/contact.component';
|
||||
|
||||
// Toutes les routes de l'application sont définies ici
|
||||
const routes: Routes = [
|
||||
{ path: '', component: LandingPageComponent },
|
||||
{ path: 'editor', component: EditorComponent },
|
||||
{ path: 'output', component: OutputComponent },
|
||||
{ path: 'documentation', component: DocumentationComponent},
|
||||
{ path: 'contact', component: ContactComponent}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
@ -0,0 +1,9 @@
|
||||
body{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
|
||||
background-color: yellow;
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
<body [ngClass]="themeClass">
|
||||
<div [ngClass]="themeClass" class="app">
|
||||
<header>
|
||||
<app-header
|
||||
[ngClass]="themeClass"
|
||||
[themeClass]="themeClass"
|
||||
[themeService]="themeService"></app-header>
|
||||
</header>
|
||||
<body>
|
||||
|
||||
<main>
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
<header>
|
||||
<app-header></app-header>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<app-footer></app-footer>
|
||||
</footer>
|
||||
|
||||
<footer>
|
||||
<app-footer></app-footer>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -1,34 +0,0 @@
|
||||
@import '../styles';
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
transition:
|
||||
background-color 0.3s ease-in-out,
|
||||
color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
body.light-theme {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
body.dark-theme {
|
||||
background-color: #333333;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.app {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
|
||||
app-footer {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { NgClass } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
standalone: true,
|
||||
imports: [NgClass, HeaderComponent, RouterOutlet, FooterComponent],
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
themeClass = 'light-theme';
|
||||
|
||||
constructor(public themeService: ThemeService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.themeService.isDarkTheme.subscribe((isDark) => {
|
||||
this.themeClass = isDark ? 'dark-theme' : 'light-theme';
|
||||
console.log('Theme class updated:', this.themeClass);
|
||||
});
|
||||
}
|
||||
export class AppComponent {
|
||||
|
||||
toggleTheme() {
|
||||
this.themeService.toggleDarkTheme();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,34 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
export function createTranslateLoader(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||
}
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { FooterComponent } from './footer/footer.component';
|
||||
import { EditorComponent } from './editor/editor.component';
|
||||
import { OutputComponent } from './output/output.component';
|
||||
import { LandingPageComponent } from './landing-page/landing-page.component';
|
||||
import { DocumentationComponent } from './documentation/documentation.component';
|
||||
import { ContactComponent } from './contact/contact.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
EditorComponent,
|
||||
OutputComponent,
|
||||
LandingPageComponent,
|
||||
DocumentationComponent,
|
||||
ContactComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { EditorComponent } from './components/editor/editor.component';
|
||||
import { LandingPageComponent } from './components/landing-page/landing-page.component';
|
||||
import { DocumentationComponent } from './components/documentation/documentation.component';
|
||||
import { FormComponent } from './components/form/form.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { WorksListComponent } from './components/works-list/works-list.component';
|
||||
import { RegisterComponent } from './components/register/register.component';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
|
||||
// Toutes les routes de l'application sont définies ici
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: LandingPageComponent },
|
||||
{ path: 'work/:work', component: EditorComponent },
|
||||
{ path: 'works', component: WorksListComponent },
|
||||
{ path: 'editor', component: EditorComponent },
|
||||
{ path: 'editor-live/:idRoom', component: EditorComponent },
|
||||
{ path: 'documentation', component: DocumentationComponent },
|
||||
{ path: 'contact', component: FormComponent },
|
||||
{ path: 'terms-of-service', component: TermsOfServiceComponent },
|
||||
{ path: 'privacy-policy', component: PrivacyPolicyComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{ path: 'login', component: LoginComponent },
|
||||
];
|
@ -1,36 +0,0 @@
|
||||
<div class="documentation">
|
||||
<h1 class="title">Documentation</h1>
|
||||
<div class="how_it_works">
|
||||
<h2 class="question">Comment fonctionne Sandkasten ?</h2>
|
||||
<span class="text"
|
||||
>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in
|
||||
sagittis quam. Suspendisse eget posuere ligula, ut pretium sapien. Nulla
|
||||
vitae aliquam risus. Nulla facilisi. Cras sodales placerat elit, at
|
||||
ultricies dolor fringilla eget. Nullam sodales eros eget nisl dignissim,
|
||||
vel accumsan ligula euismod. Praesent congue placerat ullamcorper. Mauris
|
||||
at ante porttitor, maximus magna at, convallis velit.</span
|
||||
>
|
||||
</div>
|
||||
<div class="code_execution">
|
||||
<h2 class="question">Exécution du code :</h2>
|
||||
<span class="text"
|
||||
>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in
|
||||
sagittis quam. Suspendisse eget posuere ligula, ut pretium sapien. Nulla
|
||||
vitae aliquam risus. Nulla facilisi. Cras sodales placerat elit, at
|
||||
ultricies dolor fringilla eget. Nullam sodales eros eget nisl dignissim,
|
||||
vel accumsan ligula euismod. Praesent congue placerat ullamcorper. Mauris
|
||||
at ante porttitor, maximus magna at, convallis velit.</span
|
||||
>
|
||||
</div>
|
||||
<div class="code_transfert">
|
||||
<h2 class="question">Transmission du code :</h2>
|
||||
<span class="text"
|
||||
>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in
|
||||
sagittis quam. Suspendisse eget posuere ligula, ut pretium sapien. Nulla
|
||||
vitae aliquam risus. Nulla facilisi. Cras sodales placerat elit, at
|
||||
ultricies dolor fringilla eget. Nullam sodales eros eget nisl dignissim,
|
||||
vel accumsan ligula euismod. Praesent congue placerat ullamcorper. Mauris
|
||||
at ante porttitor, maximus magna at, convallis velit.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
@ -1,25 +0,0 @@
|
||||
.documentation {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4rem 8rem;
|
||||
|
||||
.title {
|
||||
font-size: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.how_it_works,
|
||||
.code_execution,
|
||||
.code_transfert {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.question {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
<div id="editor">
|
||||
<div id="editor-bar-header">
|
||||
<div class="editor-section-bar-header">
|
||||
<div class="param-editor">
|
||||
<label for="fileInput">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 5C1 3.34315 2.34315 2 4 2H8.55848C9.84977 2 10.9962 2.82629 11.4045 4.05132L11.7208 5H20C21.1046 5 22 5.89543 22 7V9.00961C23.1475 9.12163 23.9808 10.196 23.7695 11.3578L22.1332 20.3578C21.9603 21.3087 21.132 22 20.1654 22H3C1.89543 22 1 21.1046 1 20V5ZM20 9V7H11.7208C10.8599 7 10.0956 6.44914 9.82339 5.63246L9.50716 4.68377C9.37105 4.27543 8.98891 4 8.55848 4H4C3.44772 4 3 4.44772 3 5V12.2709L3.35429 10.588C3.54913 9.66249 4.36562 9 5.31139 9H20ZM3.36634 20C3.41777 19.9109 3.4562 19.8122 3.47855 19.706L5.31139 11L21 11H21.8018L20.1654 20L3.36634 20Z"
|
||||
fill="#000000"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
<input
|
||||
style="display: none"
|
||||
type="file"
|
||||
id="fileInput"
|
||||
(change)="loadFromFile($event)" />
|
||||
</div>
|
||||
|
||||
<div class="param-editor">
|
||||
<button class="button-icon" type="button" (click)="saveToFile()">
|
||||
<svg
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3 15C3 17.8284 3 19.2426 3.87868 20.1213C4.75736 21 6.17157 21 9 21H15C17.8284 21 19.2426 21 20.1213 20.1213C21 19.2426 21 17.8284 21 15"
|
||||
stroke="#1C274C"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 3V16M12 16L16 11.625M12 16L8 11.625"
|
||||
stroke="#1C274C"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="param-editor">
|
||||
<button
|
||||
class="btn-share"
|
||||
type="button"
|
||||
(click)="shareButtonClicked()"
|
||||
[disabled]="isLoaded">
|
||||
{{ 'Editor.Share' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- TODO mettre le bouton save dans la page work-->
|
||||
<!--TODO if userID de la session =! userID du work then btn-save.style(display : none) else btn-save.style(display : block)-->
|
||||
<div class="param-editor">
|
||||
<button
|
||||
class="btn-save"
|
||||
type="button"
|
||||
(click)="saveButtonClicked()"
|
||||
[disabled]="isLoaded">
|
||||
{{ 'Editor.Save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-section-bar-header">
|
||||
@if (errorMessage) {
|
||||
<div class="param-editor">
|
||||
<p style="color: red">{{ errorMessage }}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<button class="button-join" (click)="onCreateRoomButtonClicked()">
|
||||
Créer une salle
|
||||
</button>
|
||||
|
||||
<select id="language" [(ngModel)]="selectedLanguage">
|
||||
@for (language of languages; track language.name) {
|
||||
<option [ngValue]="language">{{ language.name }}</option>
|
||||
}
|
||||
</select>
|
||||
|
||||
<div class="param-editor">
|
||||
<button
|
||||
class="button-icon button-run"
|
||||
type="button"
|
||||
(click)="onRunButtonClicked()"
|
||||
[disabled]="isLoaded">
|
||||
<div style="margin-right: 10px">{{ 'Editor.Run' | translate }}</div>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#000000">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path
|
||||
d="M2.78 2L2 2.41v12l.78.42 9-6V8l-9-6zM3 13.48V3.35l7.6 5.07L3 13.48z"></path>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6 14.683l8.78-5.853V8L6 2.147V3.35l7.6 5.07L6 13.48v1.203z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-center">
|
||||
<div class="editor-child-element">
|
||||
<codemirror6-editor [(ngModel)]="editorContent" [extensions]="extensions">
|
||||
</codemirror6-editor>
|
||||
</div>
|
||||
<div class="editor-child-element">
|
||||
<pre id="resultDiv" [innerHTML]="resultContent | safeHTML"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,110 +0,0 @@
|
||||
@mixin btn-styles($bg-color, $font-color) {
|
||||
background-color: $bg-color;
|
||||
color: $font-color;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#editor-bar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.editor-section-bar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 30px;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button-run {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #04aa6d;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.button-join {
|
||||
background-color: #1c53bf;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: #0000f0;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/*editor*/
|
||||
|
||||
.editor-center {
|
||||
height: 700px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.editor-child-element {
|
||||
min-height: 100px;
|
||||
height: 100%;
|
||||
width: 1000px;
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
border-right: 10px solid gray;
|
||||
}
|
||||
|
||||
::ng-deep .codemirror6-editor {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.btn-run {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@include btn-styles(#04aa6d, white);
|
||||
}
|
||||
|
||||
.btn-share {
|
||||
@include btn-styles(rgb(41, 120, 184), white);
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
@include btn-styles(rgb(49, 159, 49), white);
|
||||
}
|
@ -1,305 +0,0 @@
|
||||
import { Component, Input, ViewChild } from '@angular/core';
|
||||
import { BackendService } from 'src/app/services/backendService.service';
|
||||
import { Compartment, StateEffect } from '@codemirror/state';
|
||||
import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
|
||||
import { LanguageDescription } from '@codemirror/language';
|
||||
import { CODE_DEFAULTS, LANGUAGES } from '../languages';
|
||||
import { SafeHTMLPipe } from '../../safe-html.pipe';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
keymap,
|
||||
highlightSpecialChars,
|
||||
drawSelection,
|
||||
highlightActiveLine,
|
||||
dropCursor,
|
||||
rectangularSelection,
|
||||
crosshairCursor,
|
||||
lineNumbers,
|
||||
highlightActiveLineGutter,
|
||||
gutter,
|
||||
} from '@codemirror/view';
|
||||
import { Extension, EditorState } from '@codemirror/state';
|
||||
import {
|
||||
defaultHighlightStyle,
|
||||
syntaxHighlighting,
|
||||
indentOnInput,
|
||||
bracketMatching,
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
} from '@codemirror/language';
|
||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
||||
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
|
||||
import {
|
||||
autocompletion,
|
||||
completionKeymap,
|
||||
closeBrackets,
|
||||
closeBracketsKeymap,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
import { Work } from '../../models/work.model';
|
||||
import { WorkService } from '../../services/work.service';
|
||||
import { Connection, getDocument, peerExtension } from '../../services/connection.service';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { LocationStrategy } from '@angular/common';
|
||||
|
||||
const basicSetup: Extension = (() => [
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
foldGutter(),
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
autocompletion(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
highlightSelectionMatches(),
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap,
|
||||
]),
|
||||
])();
|
||||
|
||||
@Component({
|
||||
selector: 'app-editor',
|
||||
templateUrl: './editor.component.html',
|
||||
styleUrls: ['./editor.component.scss'],
|
||||
standalone: true,
|
||||
imports: [
|
||||
CodeMirrorComponent,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
SafeHTMLPipe,
|
||||
TranslateModule,
|
||||
],
|
||||
})
|
||||
export class EditorComponent {
|
||||
isLoaded: boolean = false; // Pour vérifier si le chargement est terminé
|
||||
|
||||
readonly languages: LanguageDescription[] = LANGUAGES;
|
||||
// Mode par défaut
|
||||
private _selectedLanguage = this.languages.find(
|
||||
(lang) => lang.name === 'JavaScript'
|
||||
)!;
|
||||
get selectedLanguage(): LanguageDescription {
|
||||
return this._selectedLanguage;
|
||||
}
|
||||
|
||||
set selectedLanguage(value: LanguageDescription) {
|
||||
this._selectedLanguage = value;
|
||||
if (value.name in CODE_DEFAULTS) {
|
||||
this.editorContent =
|
||||
CODE_DEFAULTS[value.name as keyof typeof CODE_DEFAULTS];
|
||||
}
|
||||
this.selectedLanguage.load().then((language) => {
|
||||
this.codemirror.editor?.dispatch({
|
||||
effects: this.languageCompartment.reconfigure(language),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _linesNumbers: boolean = true;
|
||||
get linesNumbers() {
|
||||
return this._linesNumbers;
|
||||
}
|
||||
|
||||
set linesNumbers(lines: boolean) {
|
||||
this._linesNumbers = lines;
|
||||
this.codemirror.editor?.dispatch({
|
||||
effects: this.gutterCompartment.reconfigure(
|
||||
lines ? lineNumbers() : gutter({})
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Contenu de l'éditeur que l'on passera au serveur
|
||||
editorContent: string =
|
||||
CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
|
||||
resultContent: string = '';
|
||||
|
||||
// Message d'erreur
|
||||
errorMessage: string = '';
|
||||
|
||||
@ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent;
|
||||
|
||||
private readonly languageCompartment = new Compartment();
|
||||
private readonly gutterCompartment = new Compartment();
|
||||
protected readonly extensions: Extension[] = [
|
||||
basicSetup,
|
||||
this.gutterCompartment.of(lineNumbers()),
|
||||
this.languageCompartment.of(this.selectedLanguage.support!),
|
||||
];
|
||||
|
||||
private client: WebSocket | undefined;
|
||||
|
||||
@Input()
|
||||
set idRoom(idRoom: string) {
|
||||
if (idRoom === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.client = new WebSocket(`${environment.webSocketUrl}/live/${idRoom}`);
|
||||
this.client.addEventListener('open', async () => {
|
||||
let conn = new Connection(this.client!);
|
||||
let { version, doc } = await getDocument(conn);
|
||||
|
||||
this.codemirror.editor?.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: this.codemirror.editor.state.doc.length,
|
||||
insert: doc,
|
||||
},
|
||||
});
|
||||
this.codemirror.editor?.dispatch({
|
||||
effects: StateEffect.appendConfig.of([peerExtension(version, conn)]),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#work: Work | undefined;
|
||||
|
||||
@Input()
|
||||
set work(work: string) {
|
||||
if (work === undefined) {
|
||||
return;
|
||||
}
|
||||
this.workService.getWorkByLink(work).subscribe((work) => {
|
||||
if (!work) {
|
||||
return;
|
||||
}
|
||||
this.#work = work;
|
||||
this.selectedLanguage = this.languages.find(
|
||||
(lang) => lang.name === work.language
|
||||
)!;
|
||||
this.editorContent = work.content;
|
||||
this.codemirror.editor?.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: this.codemirror.editor.state.doc.length,
|
||||
insert: work.content,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private backendService: BackendService,
|
||||
private workService: WorkService,
|
||||
private locationStrategy: LocationStrategy,
|
||||
) {
|
||||
backendService.getResult().subscribe((msg) => {
|
||||
if (msg.type === 'stdout' || msg.type === 'stderr') {
|
||||
this.resultContent += msg.text;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Efface le contenu de l'éditeur
|
||||
clear(): void {
|
||||
this.editorContent = '';
|
||||
}
|
||||
|
||||
async onCreateRoomButtonClicked() {
|
||||
const idRoom = await this.backendService.createRoom(this.editorContent);
|
||||
await this.router.navigate([`./editor-live/${idRoom}`]);
|
||||
}
|
||||
|
||||
onRunButtonClicked() {
|
||||
// Le code à exécuter est le contenu de l'éditeur
|
||||
const codeToExecute = this.editorContent;
|
||||
|
||||
this.backendService.executeCode(codeToExecute, this.selectedLanguage.name);
|
||||
|
||||
this.resultContent = '';
|
||||
}
|
||||
|
||||
loadFromFile(event: Event) {
|
||||
const file = (event.target as HTMLInputElement).files![0];
|
||||
for (const language of this.languages) {
|
||||
if (language.extensions.some((ext) => file.name.endsWith(`.${ext}`))) {
|
||||
this.selectedLanguage = language;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
this.editorContent = event.target!.result as string;
|
||||
this.errorMessage = '';
|
||||
};
|
||||
reader.readAsText(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const extensions = this.languages.flatMap((lang) => lang.extensions);
|
||||
this.errorMessage = `Unsupported language. Please select one of the following languages: ${extensions.join(', ')}.`;
|
||||
console.error(this.errorMessage);
|
||||
}
|
||||
|
||||
saveToFile() {
|
||||
const blob = new Blob([this.editorContent], { type: 'text/plain' });
|
||||
const a = document.createElement('a');
|
||||
a.download = `code.${this.selectedLanguage.extensions![0]}`;
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.click();
|
||||
}
|
||||
|
||||
addToDatabase(): void {
|
||||
this.workService.postWork(
|
||||
this.editorContent,
|
||||
this.selectedLanguage.name,
|
||||
).subscribe((link) => {
|
||||
this.prepareClipboard(link);
|
||||
});
|
||||
}
|
||||
|
||||
shareButtonClicked() {
|
||||
if (this.#work) {
|
||||
this.prepareClipboard(this.#work.link);
|
||||
} else {
|
||||
this.addToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
prepareClipboard(link: string) {
|
||||
const path = this.locationStrategy.prepareExternalUrl(`/work/${link}`);
|
||||
const url = new URL(path, window.location.href);
|
||||
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard
|
||||
.writeText(url.toString())
|
||||
.then(() => {
|
||||
alert('URL copied to clipboard!');
|
||||
});
|
||||
} else {
|
||||
alert('Clipboard API not available');
|
||||
}
|
||||
}
|
||||
|
||||
saveButtonClicked() {
|
||||
if (this.#work) {
|
||||
this.workService.updateWork(
|
||||
this.#work.id_work.toString(),
|
||||
this.editorContent,
|
||||
this.selectedLanguage.name
|
||||
);
|
||||
} else {
|
||||
this.workService.postWork(
|
||||
this.editorContent,
|
||||
this.selectedLanguage.name,
|
||||
).subscribe((link) => {
|
||||
const url = `/work/${link}`;
|
||||
this.router.navigateByUrl(url);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<head>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Podkova:wght@400;700&display=swap"
|
||||
rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<div class="footer">
|
||||
<span class="footer--rights">{{ 'FooterPage.Rights' | translate }}</span>
|
||||
<span class="footer__links">
|
||||
<a
|
||||
routerLink="terms-of-service"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'FooterPage.Legal' | translate }}</a
|
||||
>
|
||||
-
|
||||
<a
|
||||
routerLink="privacy-policy"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'FooterPage.Privacy' | translate }}</a
|
||||
>
|
||||
-
|
||||
<a
|
||||
routerLink="terms-of-service"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'FooterPage.Terms' | translate }}</a
|
||||
>
|
||||
</span>
|
||||
</div>
|
@ -1,26 +0,0 @@
|
||||
@import '../../../styles';
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0 4rem 4rem;
|
||||
text-align: center;
|
||||
|
||||
&--rights {
|
||||
font-size: 0.875rem;
|
||||
color: $color-gray;
|
||||
}
|
||||
|
||||
&__links {
|
||||
font-size: 0.75rem;
|
||||
color: $color-gray;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: $color-gray;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss'],
|
||||
standalone: true,
|
||||
imports: [RouterLink, RouterLinkActive, TranslateModule],
|
||||
})
|
||||
export class FooterComponent {
|
||||
sandkasten_logo: string = 'assets/img/logo.png';
|
||||
twitter_logo: string = 'assets/img/twitter.svg';
|
||||
instagram_logo: string = 'assets/img/instagram.svg';
|
||||
facebook_logo: string = 'assets/img/facebook.svg';
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
<div class="contact">
|
||||
<h1 class="title">Nous contacter</h1>
|
||||
|
||||
<div [formGroup]="form" class="form">
|
||||
<div>
|
||||
<!-- <label for="name">Nom</label>-->
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Votre nom"
|
||||
required
|
||||
formControlName="from_name" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- <label for="email">Email</label>-->
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Votre email"
|
||||
required
|
||||
formControlName="from_email" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- <label for="subject">Sujet</label>-->
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
placeholder="Sujet"
|
||||
required
|
||||
formControlName="subject" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- <label for="message">Message</label>-->
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Votre message"
|
||||
required
|
||||
formControlName="message"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="submit_button">
|
||||
<button
|
||||
class="submit_btn"
|
||||
type="submit"
|
||||
(click)="send()"
|
||||
[disabled]="!form.valid">
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,53 +0,0 @@
|
||||
.contact {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 2rem 8rem;
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
|
||||
& input,
|
||||
& textarea {
|
||||
width: 100%;
|
||||
min-height: 2rem;
|
||||
}
|
||||
|
||||
& textarea {
|
||||
resize: none;
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
.submit_button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
& button {
|
||||
border: none;
|
||||
background-color: #1976d2;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
width: 80%;
|
||||
height: 2.5rem;
|
||||
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
|
||||
transition: scale 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
scale: 1.05;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import emailjs from '@emailjs/browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-form',
|
||||
templateUrl: './form.component.html',
|
||||
styleUrls: ['./form.component.scss'],
|
||||
standalone: true,
|
||||
imports: [ReactiveFormsModule],
|
||||
})
|
||||
export class FormComponent {
|
||||
form: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.form = this.fb.group({
|
||||
from_name: [''],
|
||||
to_name: ['Sandkasten'],
|
||||
from_email: [''],
|
||||
subject: [''],
|
||||
message: [''],
|
||||
});
|
||||
}
|
||||
|
||||
async send() {
|
||||
emailjs.init('cLIr6GuwhkrH1JFio');
|
||||
try {
|
||||
const response = await emailjs.send(
|
||||
'service_rvmwu94',
|
||||
'template_q0spe61',
|
||||
{
|
||||
from_name: this.form.value.from_name,
|
||||
to_name: this.form.value.to_name,
|
||||
message: this.form.value.message,
|
||||
from_email: this.form.value.from_email,
|
||||
subject: this.form.value.subject,
|
||||
}
|
||||
);
|
||||
|
||||
console.log('Envoi du message:', response);
|
||||
|
||||
alert('Message envoyé avec succès !');
|
||||
this.form.reset();
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de l'envoi du message:", error);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
<div class="header" role="banner" [ngClass]="themeClass">
|
||||
<div
|
||||
class="overlay"
|
||||
[ngClass]="{ visible: isMenuOpen }"
|
||||
(click)="openCloseMenu()"></div>
|
||||
|
||||
<div class="left_part">
|
||||
<div class="left_part__logo--container">
|
||||
<a routerLink="/" routerLinkActive="active">
|
||||
<img
|
||||
class="left_part__logo--sandkasten"
|
||||
ngSrc="assets/img/logo.png"
|
||||
width="64"
|
||||
height="64"
|
||||
alt="Logo Sandkasten" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right_part">
|
||||
<div
|
||||
class="right_part--menu_mobile"
|
||||
(click)="openCloseMenu()"
|
||||
(keydown)="openCloseMenu()"
|
||||
tabindex="0">
|
||||
Menu
|
||||
</div>
|
||||
|
||||
<nav class="right_part--menu">
|
||||
<a
|
||||
routerLink="/works"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Work' | translate }}</a
|
||||
>
|
||||
|
||||
<a
|
||||
routerLink="editor"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Editor' | translate }}</a
|
||||
>
|
||||
|
||||
<a
|
||||
routerLink="documentation"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Documentation' | translate }}</a
|
||||
>
|
||||
|
||||
<a
|
||||
routerLink="contact"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Contact' | translate }}</a
|
||||
>
|
||||
</nav>
|
||||
|
||||
<div class="right_part--bottom">
|
||||
<div class="right_part--toggles">
|
||||
<div class="wrapper">
|
||||
<div class="toggle">
|
||||
<input
|
||||
class="toggle-input"
|
||||
type="checkbox"
|
||||
(click)="toggleTheme()"
|
||||
[checked]="isCheck" />
|
||||
<div class="toggle-bg"></div>
|
||||
<div class="toggle-switch">
|
||||
<div class="toggle-switch-figure"></div>
|
||||
<div class="toggle-switch-figureAlt"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<select class="right_part__lang" (change)="onLanguageChange($event)">
|
||||
<option value="fr">🇫🇷 Français</option>
|
||||
<option value="en">🇬🇧 English</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="right_part__connexion">
|
||||
<!--Login-->
|
||||
<div
|
||||
class="right_part__connexion--login"
|
||||
routerLink="/login"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span>Log In</span>
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_67_222)">
|
||||
<path
|
||||
d="M15 28.8462C11.4456 28.8462 8.214 27.495 5.7624 25.2936C7.1148 22.0764 10.2576 20.3334 14.9862 20.3334C19.7196 20.3334 22.8696 22.1598 24.231 25.3842C21.7806 27.5796 18.5484 28.8462 15 28.8462ZM15.1668 7.6662C18.123 7.6662 20.3334 9.9534 20.3334 12.993C20.3334 16.119 18.0084 18.6666 15.1668 18.6666C12.3252 18.6666 10.0002 16.119 10.0002 12.993C10.0002 9.9534 12.2106 7.6662 15.1668 7.6662ZM15 0C6.7158 0 0 6.7158 0 15C0 15.549 0.0336 16.0896 0.0912 16.623C0.1104 16.8 0.1458 16.9716 0.171 17.1468C0.222 17.5002 0.2748 17.8524 0.3498 18.1974C0.3936 18.4002 0.4512 18.5964 0.5034 18.7956C0.5844 19.1046 0.6672 19.4118 0.7668 19.713C0.8352 19.9182 0.9126 20.118 0.9888 20.319C1.0968 20.6046 1.2102 20.886 1.335 21.1626C1.4244 21.3612 1.5198 21.5568 1.6182 21.7506C1.7532 22.0182 1.896 22.2804 2.0466 22.5384C2.1558 22.7256 2.2656 22.9098 2.3826 23.0916C2.5458 23.3454 2.7186 23.5908 2.8962 23.8338C3.0204 24.0042 3.1422 24.1746 3.2736 24.3384C3.4686 24.5832 3.6762 24.816 3.8856 25.0476C4.0176 25.194 4.1448 25.344 4.2828 25.4844C4.5258 25.7334 4.7826 25.965 5.0418 26.196C5.1456 26.2884 5.2392 26.391 5.346 26.481L5.3484 26.4744C8.04719 28.7544 11.467 30.0036 15 30C18.5284 30.004 21.9442 28.7581 24.6414 26.4834C24.6421 26.4854 24.6427 26.4874 24.6432 26.4894C24.7464 26.403 24.8376 26.304 24.9372 26.2146C25.2024 25.9794 25.4652 25.7424 25.713 25.4892C25.8498 25.35 25.9752 25.2018 26.106 25.0572C26.3172 24.8238 26.5266 24.5892 26.7228 24.3426C26.8536 24.1794 26.9754 24.009 27.099 23.8398C27.2772 23.5968 27.4512 23.3502 27.6144 23.0958C28.0087 22.4779 28.3592 21.8331 28.6632 21.1662C28.788 20.8896 28.902 20.6082 29.0106 20.3226C29.0868 20.121 29.1642 19.9212 29.232 19.7154C29.3322 19.4142 29.415 19.107 29.496 18.7974C29.5482 18.5982 29.6058 18.402 29.6496 18.1998C29.7252 17.8542 29.778 17.5014 29.829 17.1474C29.8542 16.9722 29.8896 16.8012 29.9088 16.6242C29.9658 16.0902 30 15.5496 30 15C30 6.7158 23.2842 0 15 0Z"
|
||||
fill="#605FFC" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_67_222">
|
||||
<rect width="30" height="30" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<!--Register-->
|
||||
<button
|
||||
class="right_part__connexion--register"
|
||||
routerLink="/register"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span>Register Now</span>
|
||||
</button>
|
||||
<!-- Logout -->
|
||||
<button class="right_part__connexion--logout" (click)="logout()">
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile_menu" [ngClass]="{ opened: isMenuOpen }" #menuRef>
|
||||
<div class="mobile_menu--wrapper">
|
||||
<div class="mobile_menu--toggles">
|
||||
<div class="wrapper">
|
||||
<div class="toggle">
|
||||
<input
|
||||
class="toggle-input"
|
||||
type="checkbox"
|
||||
(click)="toggleTheme()"
|
||||
[checked]="isCheck" />
|
||||
<div class="toggle-bg"></div>
|
||||
<div class="toggle-switch">
|
||||
<div class="toggle-switch-figure"></div>
|
||||
<div class="toggle-switch-figureAlt"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<select class="right_part__lang" (change)="onLanguageChange($event)">
|
||||
<option value="fr">🇫🇷 Français</option>
|
||||
<option value="en">🇬🇧 English</option>
|
||||
</select>
|
||||
</div>
|
||||
<nav class="mobile_menu--menu">
|
||||
<a
|
||||
routerLink="editor"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Editor' | translate }}</a
|
||||
>
|
||||
|
||||
<a
|
||||
routerLink="documentation"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Documentation' | translate }}</a
|
||||
>
|
||||
|
||||
<a
|
||||
routerLink="contact"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>{{ 'HeaderPage.Contact' | translate }}</a
|
||||
>
|
||||
</nav>
|
||||
<div class="mobile_menu__connexion">
|
||||
<!--Login-->
|
||||
<div
|
||||
class="right_part__connexion--login"
|
||||
routerLink="/login"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span>Log In</span>
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_67_222)">
|
||||
<path
|
||||
d="M15 28.8462C11.4456 28.8462 8.214 27.495 5.7624 25.2936C7.1148 22.0764 10.2576 20.3334 14.9862 20.3334C19.7196 20.3334 22.8696 22.1598 24.231 25.3842C21.7806 27.5796 18.5484 28.8462 15 28.8462ZM15.1668 7.6662C18.123 7.6662 20.3334 9.9534 20.3334 12.993C20.3334 16.119 18.0084 18.6666 15.1668 18.6666C12.3252 18.6666 10.0002 16.119 10.0002 12.993C10.0002 9.9534 12.2106 7.6662 15.1668 7.6662ZM15 0C6.7158 0 0 6.7158 0 15C0 15.549 0.0336 16.0896 0.0912 16.623C0.1104 16.8 0.1458 16.9716 0.171 17.1468C0.222 17.5002 0.2748 17.8524 0.3498 18.1974C0.3936 18.4002 0.4512 18.5964 0.5034 18.7956C0.5844 19.1046 0.6672 19.4118 0.7668 19.713C0.8352 19.9182 0.9126 20.118 0.9888 20.319C1.0968 20.6046 1.2102 20.886 1.335 21.1626C1.4244 21.3612 1.5198 21.5568 1.6182 21.7506C1.7532 22.0182 1.896 22.2804 2.0466 22.5384C2.1558 22.7256 2.2656 22.9098 2.3826 23.0916C2.5458 23.3454 2.7186 23.5908 2.8962 23.8338C3.0204 24.0042 3.1422 24.1746 3.2736 24.3384C3.4686 24.5832 3.6762 24.816 3.8856 25.0476C4.0176 25.194 4.1448 25.344 4.2828 25.4844C4.5258 25.7334 4.7826 25.965 5.0418 26.196C5.1456 26.2884 5.2392 26.391 5.346 26.481L5.3484 26.4744C8.04719 28.7544 11.467 30.0036 15 30C18.5284 30.004 21.9442 28.7581 24.6414 26.4834C24.6421 26.4854 24.6427 26.4874 24.6432 26.4894C24.7464 26.403 24.8376 26.304 24.9372 26.2146C25.2024 25.9794 25.4652 25.7424 25.713 25.4892C25.8498 25.35 25.9752 25.2018 26.106 25.0572C26.3172 24.8238 26.5266 24.5892 26.7228 24.3426C26.8536 24.1794 26.9754 24.009 27.099 23.8398C27.2772 23.5968 27.4512 23.3502 27.6144 23.0958C28.0087 22.4779 28.3592 21.8331 28.6632 21.1662C28.788 20.8896 28.902 20.6082 29.0106 20.3226C29.0868 20.121 29.1642 19.9212 29.232 19.7154C29.3322 19.4142 29.415 19.107 29.496 18.7974C29.5482 18.5982 29.6058 18.402 29.6496 18.1998C29.7252 17.8542 29.778 17.5014 29.829 17.1474C29.8542 16.9722 29.8896 16.8012 29.9088 16.6242C29.9658 16.0902 30 15.5496 30 15C30 6.7158 23.2842 0 15 0Z"
|
||||
fill="#605FFC" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_67_222">
|
||||
<rect width="30" height="30" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<!--Register-->
|
||||
<button
|
||||
class="right_part__connexion--register"
|
||||
routerLink="/register"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span>Register Now</span>
|
||||
</button>
|
||||
<!-- Logout -->
|
||||
<button class="right_part__connexion--logout" (click)="logout()">
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,422 +0,0 @@
|
||||
@import '../../../styles';
|
||||
|
||||
//region Colors
|
||||
$color-sun: #e4c74d;
|
||||
$color-cloud-inner: #ffffff;
|
||||
$color-cloud-outer: #d4d4d2;
|
||||
$color-parent-outer: #81c0d5;
|
||||
$color-parent-inner: #c0e6f6;
|
||||
$color-moon-inner: #fffdf2;
|
||||
$color-moon-outer: #dee1c5;
|
||||
$color-stars: #fcfcfc;
|
||||
// endregion
|
||||
|
||||
//region Mixins
|
||||
@mixin crater($top, $left, $size) {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: $top;
|
||||
left: $left;
|
||||
width: $size;
|
||||
height: $size;
|
||||
background-color: #efeeda;
|
||||
border-radius: 100%;
|
||||
border: 4px solid $color-moon-outer;
|
||||
}
|
||||
|
||||
@mixin cloudBubble($top, $right, $width, $height, $deg) {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
top: $top;
|
||||
right: $right;
|
||||
width: $width;
|
||||
height: $height;
|
||||
border: 8px solid $color-cloud-outer;
|
||||
border-radius: 100%;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
transform: rotateZ($deg);
|
||||
background-color: $color-cloud-inner;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
$theme-transition: all 0.3s ease-in-out;
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(black, 0.2);
|
||||
display: none;
|
||||
|
||||
&.visible {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.left_part {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&__logo {
|
||||
&--container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
&--sandkasten {
|
||||
width: 4rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right_part,
|
||||
.mobile_menu {
|
||||
// Dark mode button
|
||||
.wrapper {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 4px;
|
||||
border-radius: 40px;
|
||||
scale: 0.75;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.toggle-bg {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $color-parent-inner;
|
||||
border-radius: 40px;
|
||||
border: 4px solid $color-parent-outer;
|
||||
transition: all 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.toggle-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid red;
|
||||
border-radius: 40px;
|
||||
z-index: 2;
|
||||
opacity: 0;
|
||||
|
||||
&:checked ~ .toggle-switch {
|
||||
margin-left: 0;
|
||||
border-color: $color-moon-outer;
|
||||
background-color: $color-moon-inner;
|
||||
}
|
||||
|
||||
&:checked ~ .toggle-bg {
|
||||
background-color: #484848;
|
||||
border-color: #202020;
|
||||
}
|
||||
|
||||
&:checked ~ .toggle-switch .toggle-switch-figure {
|
||||
margin-left: 40px;
|
||||
opacity: 0;
|
||||
transform: scale(0.1);
|
||||
}
|
||||
|
||||
&:checked ~ .toggle-switch .toggle-switch-figureAlt {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-left: 60px;
|
||||
background-color: #f5eb42;
|
||||
border: 4px solid $color-sun;
|
||||
border-radius: 50%;
|
||||
transition: all 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
.toggle-switch-figure {
|
||||
position: absolute;
|
||||
bottom: -14px;
|
||||
left: -50px;
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
border: 8px solid $color-cloud-outer;
|
||||
border-radius: 20px;
|
||||
background-color: #fff;
|
||||
transform: scale(0.4);
|
||||
transition: all 0.12s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
&:after {
|
||||
@include cloudBubble(-65px, -42px, 15px, 15px, 70deg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
@include cloudBubble(-25px, -10px, 30px, 30px, 30deg);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-switch-figureAlt {
|
||||
@include crater(5px, 2px, 2px);
|
||||
|
||||
box-shadow:
|
||||
42px -7px 0 -3px $color-stars,
|
||||
75px -10px 0 -3px $color-stars,
|
||||
54px 4px 0 -4px $color-stars,
|
||||
83px 7px 0 -2px $color-stars,
|
||||
63px 18px 0 -4px $color-stars,
|
||||
44px 23px 0 -2px $color-stars,
|
||||
78px 21px 0 -3px $color-stars;
|
||||
|
||||
transition: all 0.12s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transform: scale(0);
|
||||
|
||||
&:before {
|
||||
@include crater(-6px, 12px, 7px);
|
||||
}
|
||||
|
||||
&:after {
|
||||
@include crater(10px, 6px, 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right_part {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
|
||||
&--menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
|
||||
a {
|
||||
color: $color-black;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
transition: $theme-transition;
|
||||
}
|
||||
}
|
||||
|
||||
&--menu_mobile {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&--bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--toggles {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__lang {
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
&__connexion {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
|
||||
&--login {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
color: $color-purple;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&--register {
|
||||
cursor: pointer;
|
||||
border: 1px solid $color-black;
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem 2rem;
|
||||
white-space: nowrap;
|
||||
|
||||
transition: border 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile_menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
height: 100vh;
|
||||
background: $color-white;
|
||||
transform: translateX(100%);
|
||||
z-index: 99;
|
||||
|
||||
transition:
|
||||
transform 0.3s ease-in-out,
|
||||
box-shadow 0.3s ease-in-out,
|
||||
background 0.3s ease-in-out;
|
||||
|
||||
&.opened {
|
||||
transform: translateX(0);
|
||||
box-shadow: $color-black 0 0 5px 0;
|
||||
}
|
||||
|
||||
&--wrapper {
|
||||
//padding-top: 6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3rem;
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&--toggles {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
&--menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
a {
|
||||
color: $color-black;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
transition: $theme-transition;
|
||||
}
|
||||
}
|
||||
|
||||
&__connexion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
.right_part {
|
||||
&--menu {
|
||||
a {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
&__connexion {
|
||||
&--register {
|
||||
border-color: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile_menu {
|
||||
background: $color-light-black;
|
||||
|
||||
&.opened {
|
||||
box-shadow: $color-white 0 0 5px 0;
|
||||
}
|
||||
|
||||
&--menu {
|
||||
a {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
&__connexion {
|
||||
&--register {
|
||||
border-color: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.header {
|
||||
.right_part {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
&--menu_mobile {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&--menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__connexion {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--toggles {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.header {
|
||||
.mobile_menu {
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
||||
import { TranslationService } from '../../services/translation.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { NgClass, NgOptimizedImage } from '@angular/common';
|
||||
import { UserService } from 'src/app/services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgClass,
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
NgOptimizedImage,
|
||||
],
|
||||
})
|
||||
export class HeaderComponent {
|
||||
title: string = 'Sandkasten';
|
||||
version: string = '1.0';
|
||||
isMenuOpen: boolean = false;
|
||||
isCheck: boolean = false;
|
||||
linkLastWork = ''; //peut-être à cast en string
|
||||
|
||||
@ViewChild('menuRef') menuRef!: ElementRef;
|
||||
@Input() themeClass!: string;
|
||||
@Input() themeService!: ThemeService;
|
||||
|
||||
// Instanciation du service pour les actions de traduction
|
||||
constructor(
|
||||
private router: Router,
|
||||
private translationService: TranslationService,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
// Méthode pour changer la langue
|
||||
onLanguageChange(event: Event) {
|
||||
this.translationService.onLanguageChange(
|
||||
(event.target as HTMLSelectElement).value as 'fr' | 'en'
|
||||
);
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
this.themeService.toggleDarkTheme();
|
||||
this.themeService.isDarkTheme.subscribe((value) => {
|
||||
this.isCheck = value;
|
||||
});
|
||||
}
|
||||
|
||||
openCloseMenu() {
|
||||
this.isMenuOpen = !this.isMenuOpen;
|
||||
|
||||
if (this.isMenuOpen) {
|
||||
// Add an overflow to the body
|
||||
document.body.classList.add('no-scroll');
|
||||
} else {
|
||||
// Remove the overflow of the body
|
||||
document.body.classList.remove('no-scroll');
|
||||
}
|
||||
}
|
||||
|
||||
onLastWorkLink(): void {
|
||||
const url = `/work/${this.linkLastWork}`;
|
||||
this.router.navigateByUrl(url);
|
||||
}
|
||||
|
||||
// Logout
|
||||
logout() {
|
||||
this.userService.logoutUser().subscribe((response) => {
|
||||
if (response.success) {
|
||||
console.log('Logout success');
|
||||
} else {
|
||||
console.log('Logout error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,458 +0,0 @@
|
||||
<div class="landing-page" [ngClass]="themeClass">
|
||||
<div class="wrapper">
|
||||
<!--region Hero-->
|
||||
<div class="landing-page__hero">
|
||||
<div class="landing-page__hero_left">
|
||||
<h1 class="landing-page__hero_left--title">
|
||||
{{ 'LandingPage.Welcome' | translate }}
|
||||
</h1>
|
||||
<span class="landing-page__hero_left--description">{{
|
||||
'LandingPage.Description' | translate
|
||||
}}</span>
|
||||
<div class="landing-page__hero_left__buttons">
|
||||
<button
|
||||
class="landing-page__hero_left__buttons--editor"
|
||||
routerLink="/editor">
|
||||
<img
|
||||
class="landing-page__hero_left__buttons--editor--image"
|
||||
src="assets/img/logo.png"
|
||||
alt="editor icon" />
|
||||
<span>{{ 'LandingPage.Bouton1' | translate }}</span>
|
||||
</button>
|
||||
<a href="https://codefirst.iut.uca.fr/git/sandkasten">
|
||||
<button class="landing-page__hero_left__buttons--git">
|
||||
<img
|
||||
class="landing-page__hero_left__buttons--git--image"
|
||||
src="assets/img/landing-page/gitea.png"
|
||||
alt="editor icon" />
|
||||
<span>{{ 'LandingPage.Bouton2' | translate }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="landing-page__hero_right">
|
||||
<img
|
||||
class="landing-page__hero_right--image"
|
||||
src="assets/img/logo.png"
|
||||
alt="Sandkasten Logo" />
|
||||
</div>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
|
||||
<!--region Informations-->
|
||||
<div class="landing-page__informations">
|
||||
<div class="landing-page__informations_top">
|
||||
<h2 class="landing-page__informations_top--title">
|
||||
{{ 'LandingPage.Top.Tittle' | translate }}
|
||||
</h2>
|
||||
<span class="landing-page__informations_top--description">{{
|
||||
'LandingPage.Top.Description' | translate
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="landing-page__informations--lists">
|
||||
<!--SECURITE-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 48 48"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<title>data-source-solid</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="invisible_box" data-name="invisible box">
|
||||
<rect width="48" height="48" fill="none" />
|
||||
</g>
|
||||
<g id="icons_Q2" data-name="icons Q2">
|
||||
<path
|
||||
d="M46,9c0-6.8-19.8-7-22-7S2,2.2,2,9v7c0,.3,1.1,1.8,5.2,3.4h.3a40.3,40.3,0,0,0,8.6,2A65.6,65.6,0,0,0,24,22a65.6,65.6,0,0,0,7.9-.5,40.3,40.3,0,0,0,8.6-2h.3C44.9,17.8,46,16.3,46,16V9.3h0ZM2,31.3V39c0,6.8,19.8,7,22,7s22-.2,22-7V31.3C41.4,34.1,33.3,36,24,36S6.6,34.1,2,31.3Zm43.7-9.8a22.5,22.5,0,0,1-4.9,2.1A54.8,54.8,0,0,1,24,26,54.8,54.8,0,0,1,7.2,23.6a22.5,22.5,0,0,1-4.9-2.1L2,21.3V26c0,.3,1.2,1.9,5.5,3.5A50.2,50.2,0,0,0,24,32a50.2,50.2,0,0,0,16.5-2.5C44.8,27.9,46,26.3,46,26V21.3Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle1' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text1' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!--Import-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 1920 1920"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill-rule="evenodd">
|
||||
<path
|
||||
d="M1352.685 168.955V733.66h564.706v1016.47H675.038v-677.647h-458.88l272.979 272.98-79.962 79.848L-.011 1016.127l409.186-409.3 79.962 79.85-272.979 272.866h458.88V168.955h677.647Zm0 790.588H787.98v112.941h564.706v-112.94Z" />
|
||||
<path d="M1465.649 620.776h434.823L1465.65 185.84z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle2' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text2' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!--Options-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
id="_x32_"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 512 512">
|
||||
<g>
|
||||
<path
|
||||
class="st0"
|
||||
d="M496,293.984c9.031-0.703,16-8.25,16-17.297v-41.375c0-9.063-6.969-16.594-16-17.313l-54.828-4.281
|
||||
c-3.484-0.266-6.484-2.453-7.828-5.688l-18.031-43.516c-1.344-3.219-0.781-6.906,1.5-9.547l35.75-41.813
|
||||
c5.875-6.891,5.5-17.141-0.922-23.547l-29.25-29.25c-6.406-6.406-16.672-6.813-23.547-0.922l-41.813,35.75
|
||||
c-2.641,2.266-6.344,2.844-9.547,1.516l-43.531-18.047c-3.219-1.328-5.422-4.375-5.703-7.828l-4.266-54.813
|
||||
C293.281,6.969,285.75,0,276.688,0h-41.375c-9.063,0-16.594,6.969-17.297,16.016l-4.281,54.813c-0.266,3.469-2.469,6.5-5.688,7.828
|
||||
l-43.531,18.047c-3.219,1.328-6.906,0.75-9.563-1.516l-41.797-35.75c-6.875-5.891-17.125-5.484-23.547,0.922l-29.25,29.25
|
||||
c-6.406,6.406-6.797,16.656-0.922,23.547l35.75,41.813c2.25,2.641,2.844,6.328,1.5,9.547l-18.031,43.516
|
||||
c-1.313,3.234-4.359,5.422-7.813,5.688L16,218c-9.031,0.719-16,8.25-16,17.313v41.359c0,9.063,6.969,16.609,16,17.313l54.844,4.266
|
||||
c3.453,0.281,6.5,2.484,7.813,5.703l18.031,43.516c1.344,3.219,0.75,6.922-1.5,9.563l-35.75,41.813
|
||||
c-5.875,6.875-5.484,17.125,0.922,23.547l29.25,29.25c6.422,6.406,16.672,6.797,23.547,0.906l41.797-35.75
|
||||
c2.656-2.25,6.344-2.844,9.563-1.5l43.531,18.031c3.219,1.344,5.422,4.359,5.688,7.844l4.281,54.813
|
||||
c0.703,9.031,8.234,16.016,17.297,16.016h41.375c9.063,0,16.594-6.984,17.297-16.016l4.266-54.813
|
||||
c0.281-3.484,2.484-6.5,5.703-7.844l43.531-18.031c3.203-1.344,6.922-0.75,9.547,1.5l41.813,35.75
|
||||
c6.875,5.891,17.141,5.5,23.547-0.906l29.25-29.25c6.422-6.422,6.797-16.672,0.922-23.547l-35.75-41.813
|
||||
c-2.25-2.641-2.844-6.344-1.5-9.563l18.031-43.516c1.344-3.219,4.344-5.422,7.828-5.703L496,293.984z M256,342.516
|
||||
c-23.109,0-44.844-9-61.188-25.328c-16.344-16.359-25.344-38.078-25.344-61.203c0-23.109,9-44.844,25.344-61.172
|
||||
c16.344-16.359,38.078-25.344,61.188-25.344c23.125,0,44.844,8.984,61.188,25.344c16.344,16.328,25.344,38.063,25.344,61.172
|
||||
c0,23.125-9,44.844-25.344,61.203C300.844,333.516,279.125,342.516,256,342.516z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle3' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text3' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!--Multi-language-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 36 36"
|
||||
version="1.1"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>language-solid</title>
|
||||
<polygon
|
||||
points="11,16.5 10,19.6 12,19.6 11,16.5 "
|
||||
class="clr-i-solid clr-i-solid-path-1"></polygon>
|
||||
<path
|
||||
d="M30.3,3h-16v5h4v2h-13c-1.7,0-3,1.3-3,3v11c0,1.7,1.3,3,3,3h1v5.1l6.3-5.1h6.7v-7h11c1.7,0,3-1.3,3-3V6
|
||||
C33.3,4.3,32,3,30.3,3z M13.1,22.9l-0.5-1.6H9.5l-0.6,1.6H6.5L9.8,14h2.4l3.3,8.9L13.1,22.9z M28.3,15v2c-1.3,0-2.7-0.4-3.9-1
|
||||
c-1.2,0.6-2.6,0.9-4,1l-0.1-2c0.7,0,1.4-0.1,2.1-0.3c-0.9-0.9-1.5-2-1.8-3.2h2.1c0.3,0.9,0.9,1.6,1.6,2.2c1.1-0.9,1.8-2.2,1.9-3.7
|
||||
h-6V8h3V6h2v2h3.3l0.1,1c0.1,2.1-0.7,4.2-2.2,5.7C27.1,14.9,27.7,15,28.3,15z"
|
||||
class="clr-i-solid clr-i-solid-path-2"></path>
|
||||
<rect x="0" y="0" width="36" height="36" fill-opacity="0" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle4' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text4' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!--TODO-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 1920 1920"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill-rule="evenodd">
|
||||
<path
|
||||
d="M1352.685 168.955V733.66h564.706v1016.47H675.038v-677.647h-458.88l272.979 272.98-79.962 79.848L-.011 1016.127l409.186-409.3 79.962 79.85-272.979 272.866h458.88V168.955h677.647Zm0 790.588H787.98v112.941h564.706v-112.94Z" />
|
||||
<path d="M1465.649 620.776h434.823L1465.65 185.84z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle5' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text5' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!--TODO-->
|
||||
<div class="landing-page__informations__list">
|
||||
<div class="landing-page__informations__list--icon">
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 1920 1920"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill-rule="evenodd">
|
||||
<path
|
||||
d="M1352.685 168.955V733.66h564.706v1016.47H675.038v-677.647h-458.88l272.979 272.98-79.962 79.848L-.011 1016.127l409.186-409.3 79.962 79.85-272.979 272.866h458.88V168.955h677.647Zm0 790.588H787.98v112.941h564.706v-112.94Z" />
|
||||
<path d="M1465.649 620.776h434.823L1465.65 185.84z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="landing-page__informations__list--title">
|
||||
{{ 'LandingPage.List.Tittle6' | translate }}
|
||||
</div>
|
||||
<div class="landing-page__informations__list--description">
|
||||
{{ 'LandingPage.List.Text6' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
|
||||
<!--region Technologies-->
|
||||
<div class="landing-page__technologies">
|
||||
<span class="landing-page__technologies--title">{{
|
||||
'LandingPage.Technologie.Tittle' | translate
|
||||
}}</span>
|
||||
<div class="landing-page__technologies__list">
|
||||
<!--C-->
|
||||
<a href="https://www.open-std.org/jtc1/sc22/wg14/">
|
||||
<svg
|
||||
width="46"
|
||||
height="48"
|
||||
viewBox="0 0 46 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M32.9859 28.2029L45.5311 31.9948C44.6881 35.5133 43.3612 38.4524 41.5494 40.8117C39.7379 43.1703 37.4888 44.9512 34.8028 46.152C32.1166 47.353 28.6988 47.9534 24.548 47.9534C19.5131 47.9534 15.4 47.2213 12.2086 45.758C9.01669 44.295 6.26194 41.7221 3.94472 38.0373C1.62759 34.3534 0.46875 29.6369 0.46875 23.889C0.46875 16.2261 2.50725 10.3369 6.58369 6.22031C10.6597 2.10516 16.427 0.046875 23.885 0.046875C29.7206 0.046875 34.3075 1.22719 37.6466 3.58622C40.9862 5.94544 43.4662 9.56953 45.0891 14.4564L32.4486 17.2689C32.0061 15.8581 31.5426 14.8254 31.0584 14.1724C30.2575 13.0771 29.2777 12.2339 28.1193 11.6442C26.9605 11.0547 25.6647 10.7594 24.2326 10.7594C20.9879 10.7594 18.502 12.0644 16.7745 14.6735C15.4685 16.6098 14.8157 19.6501 14.8157 23.7945C14.8157 28.9291 15.5947 32.4486 17.1539 34.3525C18.7127 36.2565 20.9037 37.209 23.727 37.209C26.4656 37.209 28.5349 36.4394 29.9362 34.9023C31.3372 33.3649 32.3537 31.1313 32.9859 28.2029Z"
|
||||
fill="black" />
|
||||
</svg>
|
||||
</a>
|
||||
<!--C++-->
|
||||
<a href="https://isocpp.org/">
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M42.879 24.7409H41.3985V26.2214H39.918V24.7409H38.4375V23.2604H39.918V21.7799H41.3985V23.2604H42.879V24.7409ZM37.3275 24.7409H35.847V26.2214H34.3665V24.7409H32.886V23.2604H34.3665V21.7799H35.847V23.2604H37.3275V24.7409ZM24 37.3274C16.6395 37.3259 10.674 31.3604 10.674 23.9999C10.674 16.6394 16.6409 10.6724 24.0015 10.6724C28.9065 10.6724 33.1935 13.3229 35.5065 17.2694L35.541 17.3324L29.775 20.6684C28.602 18.6599 26.4555 17.3324 24.0015 17.3324C20.3205 17.3324 17.3355 20.3159 17.3355 23.9984C17.3355 27.6809 20.319 30.6644 24.0015 30.6644C26.4555 30.6644 28.6005 29.3384 29.757 27.3629L29.7735 27.3314L35.5425 30.6674C33.1875 34.6709 28.902 37.3184 24 37.3274ZM43.4835 12.7529C43.1865 12.2189 42.7725 11.7839 42.276 11.4689L42.261 11.4599L25.7355 1.91839C25.2315 1.65739 24.636 1.50439 24.003 1.50439C23.37 1.50439 22.7745 1.65739 22.2495 1.92739L22.2705 1.91689L5.74195 11.4584C4.74745 12.1154 4.08295 13.2029 4.01245 14.4479V14.4584V33.5399C4.08445 34.7954 4.74895 35.8814 5.72845 36.5309L5.74345 36.5399L22.269 46.0814C22.773 46.3409 23.37 46.4939 24.0015 46.4939C24.633 46.4939 25.23 46.3409 25.755 46.0724L25.734 46.0829L42.2595 36.5414C43.254 35.8844 43.917 34.7969 43.9875 33.5519V33.5414V14.4599C43.971 13.8269 43.785 13.2404 43.476 12.7394L43.485 12.7544L43.4835 12.7529Z"
|
||||
fill="black" />
|
||||
</svg>
|
||||
</a>
|
||||
<!--TS-->
|
||||
<a href="https://www.typescriptlang.org/">
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_119_4)">
|
||||
<path
|
||||
d="M0 24V48H48V0H0V24ZM38.679 22.086C39.8985 22.3905 40.827 22.938 41.6865 23.82C42.1245 24.288 42.78 25.1475 42.8355 25.359C42.8505 25.422 40.7655 26.82 39.4995 27.6015C39.453 27.633 39.2655 27.438 39.0615 27.132C38.445 26.2335 37.7955 25.8435 36.804 25.773C35.3505 25.6785 34.413 26.4375 34.4205 27.711C34.413 28.023 34.4835 28.3365 34.623 28.6095C34.944 29.274 35.5455 29.6715 37.404 30.477C40.842 31.953 42.3105 32.9295 43.224 34.3125C44.247 35.859 44.4735 38.328 43.7865 40.1715C43.0215 42.1635 41.13 43.5225 38.4735 43.968C37.653 44.1165 35.7 44.0925 34.8165 43.929C32.8875 43.5855 31.059 42.6315 29.934 41.382C29.4885 40.8975 28.629 39.624 28.6845 39.5385C28.7085 39.507 28.9035 39.3825 29.1225 39.2565C29.3415 39.1305 30.1455 38.6625 30.9045 38.2245L32.2875 37.4205L32.577 37.8495C32.9835 38.466 33.8655 39.3105 34.398 39.591C35.9295 40.404 38.031 40.287 39.0705 39.357C39.492 39.006 39.7275 38.466 39.696 37.92C39.696 37.365 39.6255 37.116 39.336 36.7005C38.961 36.1695 38.2035 35.7165 36.039 34.779C33.5625 33.708 32.4915 33.0525 31.524 31.998C30.915 31.302 30.462 30.483 30.204 29.5995C30.024 28.92 29.9775 27.216 30.1185 26.5365C30.627 24.138 32.439 22.4745 35.04 21.9825C35.8845 21.819 37.8525 21.8805 38.6805 22.0845L38.679 22.086ZM27.414 24.0945L27.429 26.055H21.1785V43.8135H16.7565V26.055H10.515V24.1335C10.515 23.0625 10.539 22.173 10.569 22.149C10.593 22.1175 14.3895 22.1025 18.999 22.11L27.39 22.134L27.414 24.0945Z"
|
||||
fill="black" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_119_4">
|
||||
<rect width="48" height="48" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</a>
|
||||
<!--JS-->
|
||||
<a href="https://developer.mozilla.org/fr/docs/Web/JavaScript">
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_120_9)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M36.7872 44.2371C32.5992 44.2371 29.892 42.2405 28.572 39.6293L32.244 37.5023C33.2112 39.0815 34.4664 40.241 36.6912 40.241C38.5584 40.241 39.7512 39.3068 39.7512 38.018C39.7512 36.0788 37.164 35.3211 35.3376 34.5387C32.0832 33.1539 29.9232 31.4139 29.9232 27.7395C29.9232 24.3579 32.5008 21.7805 36.5304 21.7805C39.396 21.7805 41.46 22.7787 42.9408 25.3875L39.4296 27.6434C38.6568 26.2586 37.8192 25.7109 36.5304 25.7109C35.208 25.7109 34.3704 26.549 34.3704 27.6434C34.3704 28.997 35.208 29.5444 37.1424 30.382C41.196 32.1196 44.2632 33.4847 44.2632 37.9535C44.2632 42.0143 41.0736 44.2371 36.7872 44.2371ZM26.4 37.4379C26.4 42.1107 23.5224 44.5805 19.5264 44.5805C15.9168 44.5805 13.7544 43.2 12.6912 40.8H12.6552H12.6384H12.6288L16.2984 38.2348C17.0064 39.49 17.6448 40.3805 19.1904 40.3805C20.6736 40.3805 21.6 39.6294 21.6 37.3758V21.6H26.4V37.4379ZM0 48H48V0H0V48Z"
|
||||
fill="black" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_120_9">
|
||||
<rect width="48" height="48" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
|
||||
<!--region About-->
|
||||
<div class="landing-page__about">
|
||||
<div class="landing-page__about__titles">
|
||||
<h2 class="landing-page__about--title">
|
||||
{{ 'LandingPage.About.Tittle' | translate }}
|
||||
</h2>
|
||||
<div class="landing-page__about--description">
|
||||
{{ 'LandingPage.About.Text' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="landing-page__about__members">
|
||||
<!--Bastien-->
|
||||
<div class="landing-page__about__member">
|
||||
<div class="landing-page__about__member--image">
|
||||
<img src="assets/img/landing-page/unknown.jpeg" alt="Bastien" />
|
||||
</div>
|
||||
<div class="landing-page__about__member--texts">
|
||||
<div class="landing-page__about__member--texts--name">Bastien</div>
|
||||
<div class="landing-page__about__member--texts--role">
|
||||
{{ 'LandingPage.MemberRole' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Clement-->
|
||||
<div class="landing-page__about__member">
|
||||
<div class="landing-page__about__member--image">
|
||||
<img src="assets/img/landing-page/unknown.jpeg" alt="Bastien" />
|
||||
</div>
|
||||
<div class="landing-page__about__member--texts">
|
||||
<div class="landing-page__about__member--texts--name">Clément</div>
|
||||
<div class="landing-page__about__member--texts--role">
|
||||
{{ 'LandingPage.MemberRole' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Colin-->
|
||||
<div class="landing-page__about__member">
|
||||
<div class="landing-page__about__member--image">
|
||||
<img src="assets/img/landing-page/unknown.jpeg" alt="Bastien" />
|
||||
</div>
|
||||
<div class="landing-page__about__member--texts">
|
||||
<div class="landing-page__about__member--texts--name">Colin</div>
|
||||
<div class="landing-page__about__member--texts--role">
|
||||
{{ 'LandingPage.MemberRole' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Hugo-->
|
||||
<div class="landing-page__about__member">
|
||||
<div class="landing-page__about__member--image">
|
||||
<img src="assets/img/landing-page/unknown.jpeg" alt="Bastien" />
|
||||
</div>
|
||||
<div class="landing-page__about__member--texts">
|
||||
<div class="landing-page__about__member--texts--name">Hugo</div>
|
||||
<div class="landing-page__about__member--texts--role">
|
||||
{{ 'LandingPage.MemberRole' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Matis-->
|
||||
<div class="landing-page__about__member">
|
||||
<div class="landing-page__about__member--image">
|
||||
<img src="assets/img/landing-page/unknown.jpeg" alt="Bastien" />
|
||||
</div>
|
||||
<div class="landing-page__about__member--texts">
|
||||
<div class="landing-page__about__member--texts--name">Matis</div>
|
||||
<div class="landing-page__about__member--texts--role">
|
||||
{{ 'LandingPage.MemberRole' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
|
||||
<!--region Socials-->
|
||||
<div class="landing-page__socials">
|
||||
<span class="landing-page__socials--title">{{
|
||||
'LandingPage.SocialTittle' | translate
|
||||
}}</span>
|
||||
<div class="landing-page__socials__list">
|
||||
<!--Twitter-->
|
||||
<a href="https://twitter.com/Sandkasten3a">
|
||||
<svg
|
||||
width="48"
|
||||
height="44"
|
||||
viewBox="0 0 48 44"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_125_14)">
|
||||
<path
|
||||
d="M37.76 0H45.12L28.96 18.4L47.84 43.36H33.024L21.424 28.192L8.14397 43.36H0.783974L17.904 23.68L-0.176025 0H15.008L25.488 13.856L37.76 0ZM35.184 39.04H39.264L12.864 4.16H8.47997L35.184 39.04Z"
|
||||
fill="black" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_125_14">
|
||||
<rect width="48" height="43.36" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</a>
|
||||
<!--Instagram-->
|
||||
<a href="https://instagram.com">
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M24 36C30.6274 36 36 30.6274 36 24C36 17.3726 30.6274 12 24 12C17.3726 12 12 17.3726 12 24C12 30.6274 17.3726 36 24 36ZM24 32C28.4182 32 32 28.4182 32 24C32 19.5817 28.4182 16 24 16C19.5817 16 16 19.5817 16 24C16 28.4182 19.5817 32 24 32Z"
|
||||
fill="#0F0F0F" />
|
||||
<path
|
||||
d="M36 10C34.8954 10 34 10.8954 34 12C34 13.1046 34.8954 14 36 14C37.1046 14 38 13.1046 38 12C38 10.8954 37.1046 10 36 10Z"
|
||||
fill="#0F0F0F" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.30792 8.55212C2 11.1191 2 14.4794 2 21.2V26.8C2 33.5206 2 36.881 3.30792 39.4478C4.4584 41.7058 6.29416 43.5416 8.55212 44.692C11.1191 46 14.4794 46 21.2 46H26.8C33.5206 46 36.881 46 39.4478 44.692C41.7058 43.5416 43.5416 41.7058 44.692 39.4478C46 36.881 46 33.5206 46 26.8V21.2C46 14.4794 46 11.1191 44.692 8.55212C43.5416 6.29416 41.7058 4.4584 39.4478 3.30792C36.881 2 33.5206 2 26.8 2H21.2C14.4794 2 11.1191 2 8.55212 3.30792C6.29416 4.4584 4.4584 6.29416 3.30792 8.55212ZM26.8 6H21.2C17.7737 6 15.4445 6.00312 13.6442 6.1502C11.8905 6.29348 10.9937 6.55318 10.3681 6.87194C8.86278 7.63894 7.63894 8.86278 6.87194 10.3681C6.55318 10.9937 6.29348 11.8905 6.1502 13.6442C6.00312 15.4445 6 17.7737 6 21.2V26.8C6 30.2264 6.00312 32.5554 6.1502 34.3558C6.29348 36.1096 6.55318 37.0064 6.87194 37.632C7.63894 39.1372 8.86278 40.361 10.3681 41.128C10.9937 41.4468 11.8905 41.7066 13.6442 41.8498C15.4445 41.9968 17.7737 42 21.2 42H26.8C30.2264 42 32.5554 41.9968 34.3558 41.8498C36.1096 41.7066 37.0064 41.4468 37.632 41.128C39.1372 40.361 40.361 39.1372 41.128 37.632C41.4468 37.0064 41.7066 36.1096 41.8498 34.3558C41.9968 32.5554 42 30.2264 42 26.8V21.2C42 17.7737 41.9968 15.4445 41.8498 13.6442C41.7066 11.8905 41.4468 10.9937 41.128 10.3681C40.361 8.86278 39.1372 7.63894 37.632 6.87194C37.0064 6.55318 36.1096 6.29348 34.3558 6.1502C32.5554 6.00312 30.2264 6 26.8 6Z"
|
||||
fill="#0F0F0F" />
|
||||
</svg>
|
||||
</a>
|
||||
<!--Facebook-->
|
||||
<a href="https://www.facebook.com/profile.php?id=61555217444337">
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2.73969 0H45.2603C46.7016 0 48 1.29836 48 2.7397V45.2603C48 46.7016 46.7016 48 45.2603 48H2.73969C1.29836 48 0 46.7016 0 45.2603V2.7397C0 1.29836 1.29836 0 2.73969 0ZM34.0871 26.9519H40.7172L41.0055 20.61H34.0871V15.9262C34.0871 14.1251 34.4468 13.115 36.753 13.115H40.7887L40.934 7.20438C40.934 7.20438 39.1329 6.91612 36.5385 6.91612C30.1966 6.91612 27.3854 10.8804 27.3854 15.1329V20.61H22.7016V26.9519H27.3854V44.5362H34.0871V26.9519Z"
|
||||
fill="black" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
|
||||
<!--region Get Started-->
|
||||
<div class="landing-page__get_started">
|
||||
<h3 class="landing-page__get_started--title">
|
||||
{{ 'LandingPage.GetStarted.Tittle' | translate }}
|
||||
</h3>
|
||||
<div class="landing-page__get_started--description">
|
||||
{{ 'LandingPage.GetStarted.Description' | translate }}
|
||||
</div>
|
||||
<button class="landing-page__get_started--button" routerLink="/editor">
|
||||
{{ 'LandingPage.GetStarted.Button' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<!--endregion-->
|
||||
</div>
|
||||
</div>
|
@ -1,490 +0,0 @@
|
||||
@import '../../../styles';
|
||||
|
||||
.landing-page {
|
||||
padding: 6rem 12rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
|
||||
color: $color-black;
|
||||
transition: color 0.3s ease-in-out;
|
||||
|
||||
.wrapper {
|
||||
max-width: 80rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
//region Hero
|
||||
&__hero {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
padding-bottom: 2rem;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
&__hero_left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
&--title {
|
||||
font-size: 5rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&--description {
|
||||
font-size: 1rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.75rem;
|
||||
|
||||
&--editor,
|
||||
&--git {
|
||||
border-radius: 4px;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
&--image {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&--editor {
|
||||
background-color: $color-black;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
&--git {
|
||||
border: 2px solid $color-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__hero_right {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&--image {
|
||||
position: absolute;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Informations
|
||||
&__informations {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
|
||||
&--lists {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: grid;
|
||||
grid: 'icon title' 'icon description' / auto 1fr;
|
||||
gap: 0.5rem 1rem;
|
||||
|
||||
&--icon {
|
||||
grid-area: icon;
|
||||
height: fit-content;
|
||||
background: black;
|
||||
border-radius: 999px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
padding: 0.5rem;
|
||||
|
||||
path {
|
||||
fill: white;
|
||||
stroke: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--title {
|
||||
grid-area: title;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&--description {
|
||||
grid-area: description;
|
||||
font-size: 1rem;
|
||||
color: $color-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__informations_top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
&--title {
|
||||
margin: 0;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&--description {
|
||||
font-size: 1rem;
|
||||
color: #475569;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Technologies
|
||||
&__technologies {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
|
||||
&--title {
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
|
||||
svg {
|
||||
width: 3rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region About
|
||||
&__about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
margin-inline: 10rem;
|
||||
|
||||
&__titles {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
&--title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&--description {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
&__members {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
&__member {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
&--image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 0.8;
|
||||
object-fit: contain;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&--texts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&--name {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
&--role {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Socials
|
||||
&__socials {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
|
||||
&--title {
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
width: 3rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Get Started
|
||||
&__get_started {
|
||||
background-color: $color-black;
|
||||
padding: 3rem 5rem;
|
||||
border-radius: 10px;
|
||||
margin-inline: 6rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
|
||||
&--title {
|
||||
//white-space: nowrap;
|
||||
color: $color-white;
|
||||
font-size: 3.75rem;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&--description {
|
||||
color: #94a4b8;
|
||||
font-size: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--button {
|
||||
border: none;
|
||||
margin-top: 0.25rem;
|
||||
cursor: pointer;
|
||||
background: $color-white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
@media (max-width: 80rem) {
|
||||
//region Hero
|
||||
&__hero_left {
|
||||
&--title {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
&--description {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Informations
|
||||
&__informations_top {
|
||||
&--title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Get Started
|
||||
&__get_started {
|
||||
margin-inline: 3rem;
|
||||
|
||||
&--title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
padding: 4rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
//region Informations
|
||||
&__informations {
|
||||
&--lists {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region About
|
||||
&__about {
|
||||
margin-inline: 3rem;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Get Started
|
||||
&__get_started {
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
&--title {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
padding: 2rem;
|
||||
|
||||
//region Hero
|
||||
&__hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
&__hero_left {
|
||||
&--title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__hero_right {
|
||||
min-height: 25dvh;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Informations
|
||||
&__informations {
|
||||
&--lists {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Technologies
|
||||
&__technologies {
|
||||
&__list {
|
||||
gap: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region About
|
||||
&__about {
|
||||
margin-inline: 0;
|
||||
|
||||
&__members {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Socials
|
||||
&__socials {
|
||||
&__list {
|
||||
gap: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Get Started
|
||||
&__get_started {
|
||||
padding: 2rem;
|
||||
margin-inline: 0;
|
||||
|
||||
&--title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
&--description {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
&--title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&--title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
&.dark-theme {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NgClass } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-landing-page',
|
||||
templateUrl: './landing-page.component.html',
|
||||
styleUrls: ['./landing-page.component.scss'],
|
||||
standalone: true,
|
||||
imports: [NgClass, TranslateModule, RouterLink],
|
||||
})
|
||||
export class LandingPageComponent implements OnInit {
|
||||
themeClass!: string;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.themeService.isDarkTheme.subscribe((value) => {
|
||||
value
|
||||
? (this.themeClass = 'dark-theme')
|
||||
: (this.themeClass = 'light-theme');
|
||||
});
|
||||
}
|
||||
// Si click sur "Run", on redirige vers la page d'édition
|
||||
onContinue(): void {
|
||||
this.router.navigateByUrl('/editor');
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import {
|
||||
LanguageDescription,
|
||||
LanguageSupport,
|
||||
StreamLanguage,
|
||||
StreamParser,
|
||||
} from '@codemirror/language';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
|
||||
function legacy(parser: StreamParser<unknown>): LanguageSupport {
|
||||
return new LanguageSupport(StreamLanguage.define(parser));
|
||||
}
|
||||
|
||||
export const CODE_DEFAULTS = {
|
||||
C: /** @lang C */ `#include <stdio.h>
|
||||
int main() {
|
||||
printf("Hello, World!\\n");
|
||||
return 0;
|
||||
}`,
|
||||
'C++': /** @lang C++ */ `#include <iostream>
|
||||
int main() {
|
||||
std::cout << "Hello, World!\\n";
|
||||
return 0;
|
||||
}`,
|
||||
JavaScript: /** @lang JS */ `console.log("Hello, World!");`,
|
||||
TypeScript: /** @lang TS */ `console.log("Hello, World!");`,
|
||||
Bash: 'echo "Hello, world!"',
|
||||
};
|
||||
|
||||
export const LANGUAGES = [
|
||||
LanguageDescription.of({
|
||||
name: 'C',
|
||||
extensions: ['c', 'h', 'ino'],
|
||||
load() {
|
||||
return import('@codemirror/lang-cpp').then((m) => m.cpp());
|
||||
},
|
||||
}),
|
||||
LanguageDescription.of({
|
||||
name: 'C++',
|
||||
alias: ['cpp'],
|
||||
extensions: ['cpp', 'c++', 'cc', 'cxx', 'hpp', 'h++', 'hh', 'hxx'],
|
||||
load() {
|
||||
return import('@codemirror/lang-cpp').then((m) => m.cpp());
|
||||
},
|
||||
}),
|
||||
LanguageDescription.of({
|
||||
name: 'JavaScript',
|
||||
alias: ['ecmascript', 'js', 'node'],
|
||||
extensions: ['js', 'mjs', 'cjs'],
|
||||
support: javascript(),
|
||||
}),
|
||||
LanguageDescription.of({
|
||||
name: 'TypeScript',
|
||||
alias: ['ts'],
|
||||
extensions: ['ts'],
|
||||
load() {
|
||||
return import('@codemirror/lang-javascript').then((m) =>
|
||||
m.javascript({ typescript: true })
|
||||
);
|
||||
},
|
||||
}),
|
||||
LanguageDescription.of({
|
||||
name: 'Bash',
|
||||
alias: ['bash', 'sh', 'sh'],
|
||||
extensions: ['sh', 'ksh', 'bash'],
|
||||
filename: /^PKGBUILD$/,
|
||||
load() {
|
||||
return import('@codemirror/legacy-modes/mode/shell').then((m) =>
|
||||
legacy(m.shell)
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
@ -1,18 +0,0 @@
|
||||
.form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
color: green;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
margin-top: 20px;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<form [formGroup]="loginForm" (ngSubmit)="loginAction()">
|
||||
<mat-form-field>
|
||||
<mat-label>Login</mat-label>
|
||||
<input matInput formControlName="login" required />
|
||||
<mat-error *ngIf="loginForm.controls.login.invalid">
|
||||
Login is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Password</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[type]="hide ? 'password' : 'text'"
|
||||
formControlName="password"
|
||||
required />
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
(click)="hide = !hide"
|
||||
[attr.aria-label]="'Hide password'">
|
||||
<mat-icon>{{ hide ? 'visibility_off' : 'visibility' }}</mat-icon>
|
||||
</button>
|
||||
<mat-error *ngIf="loginForm.controls.password.invalid">
|
||||
Password is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-raised-button type="submit">Login</button>
|
||||
|
||||
<div *ngIf="errorLogin">{{ errorLogin }}</div>
|
||||
<div *ngIf="successLogin">{{ successLogin }}</div>
|
||||
</form>
|
@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoginComponent, HttpClientModule, NoopAnimationsModule],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,71 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import {
|
||||
FormControl,
|
||||
Validators,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
NgForm,
|
||||
FormGroup,
|
||||
FormBuilder,
|
||||
} from '@angular/forms';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { merge } from 'rxjs';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { UserService } from 'src/app/services/user.service';
|
||||
import { User } from 'src/app/models/user.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrl: './login.component.css',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
CommonModule,
|
||||
],
|
||||
})
|
||||
export class LoginComponent {
|
||||
hide = true;
|
||||
|
||||
loginForm = this.formBuilder.group({
|
||||
login: ['', Validators.required],
|
||||
password: ['', Validators.required],
|
||||
});
|
||||
|
||||
errorMessage = '';
|
||||
|
||||
successLogin = '';
|
||||
errorLogin = '';
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private formBuilder: FormBuilder
|
||||
) {
|
||||
}
|
||||
|
||||
loginAction() {
|
||||
const formValue = this.loginForm.value;
|
||||
|
||||
this.userService
|
||||
.loginUser(formValue.login!, formValue.password!)
|
||||
.then((response) => {
|
||||
console.log('response :', response);
|
||||
if (response.success) {
|
||||
this.successLogin = 'Vous êtes connecté.';
|
||||
this.errorLogin = '';
|
||||
} else {
|
||||
this.errorLogin = "L'authentification a échoué.";
|
||||
this.successLogin = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
<body>
|
||||
<h1>{{ 'PrivacyPolicyPage.PrivacyPolicy' | translate }}</h1>
|
||||
<p>
|
||||
{{ 'PrivacyPolicyPage.Promise' | translate
|
||||
}}<a href="https://www.sandkasten.fr">sandkasten.fr</a>
|
||||
{{ 'PrivacyPolicyPage.PromisePart2' | translate }}
|
||||
</p>
|
||||
<p>{{ 'PrivacyPolicyPage.Policy' | translate }}</p>
|
||||
<div class="wpembed-toc">
|
||||
<h3>{{ 'PrivacyPolicyPage.TableContents' | translate }}</h3>
|
||||
<ol class="wpembed-toc">
|
||||
<li>
|
||||
<a href="#collection-of-personal-information">{{
|
||||
'PrivacyPolicyPage.PersonalInformation.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#privacy-of-children">{{
|
||||
'PrivacyPolicyPage.PrivacyChildren.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#use-and-processing-of-collected-information">{{
|
||||
'PrivacyPolicyPage.UseProcInformation.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#managing-information">{{
|
||||
'PrivacyPolicyPage.ManagingInformation.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#disclosure-of-information">{{
|
||||
'PrivacyPolicyPage.DisclosureInformation.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#retention-of-information">{{
|
||||
'PrivacyPolicyPage.RetentionInformation.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#do-not-track-signals">{{
|
||||
'PrivacyPolicyPage.DoNotTrack.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#links-to-other-resources">{{
|
||||
'PrivacyPolicyPage.LinksResources.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#information-security">{{
|
||||
'PrivacyPolicyPage.InformationSecurity.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#data-breach">{{
|
||||
'PrivacyPolicyPage.DataBreach.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#changes-and-amendments">{{
|
||||
'PrivacyPolicyPage.Amendments.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#acceptance-of-this-policy">{{
|
||||
'PrivacyPolicyPage.Acceptance.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#contacting-us">{{
|
||||
'PrivacyPolicyPage.ContactingUs.Title' | translate
|
||||
}}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<h2 id="collection-of-personal-information">
|
||||
{{ 'PrivacyPolicyPage.PersonalInformation.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.PersonalInformation.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.PersonalInformation.Text2' | translate }}</p>
|
||||
<ul>
|
||||
<li>{{ 'PrivacyPolicyPage.PersonalInformation.Text3' | translate }}</li>
|
||||
<li>{{ 'PrivacyPolicyPage.PersonalInformation.Text4' | translate }}</li>
|
||||
</ul>
|
||||
<p>{{ 'PrivacyPolicyPage.PersonalInformation.Text5' | translate }}</p>
|
||||
|
||||
<h2 id="privacy-of-children">
|
||||
{{ 'PrivacyPolicyPage.PrivacyChildren.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.PrivacyChildren.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.PrivacyChildren.Text2' | translate }}</p>
|
||||
<h2 id="use-and-processing-of-collected-information">
|
||||
{{ 'PrivacyPolicyPage.UseProcInformation.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text2' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text3' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text4' | translate }}</p>
|
||||
<ul>
|
||||
<li>{{ 'PrivacyPolicyPage.UseProcInformation.List1' | translate }}</li>
|
||||
<li>{{ 'PrivacyPolicyPage.UseProcInformation.List2' | translate }}</li>
|
||||
<li>{{ 'PrivacyPolicyPage.UseProcInformation.List3' | translate }}</li>
|
||||
<li>{{ 'PrivacyPolicyPage.UseProcInformation.List4' | translate }}</li>
|
||||
</ul>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text5' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.UseProcInformation.Text6' | translate }}</p>
|
||||
|
||||
<h2 id="managing-information">
|
||||
{{ 'PrivacyPolicyPage.ManagingInformation.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.ManagingInformation.Text1' | translate }}</p>
|
||||
|
||||
<h2 id="disclosure-of-information">
|
||||
{{ 'PrivacyPolicyPage.DisclosureInformation.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.DisclosureInformation.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.DisclosureInformation.Text2' | translate }}</p>
|
||||
|
||||
<h2 id="retention-of-information">
|
||||
{{ 'PrivacyPolicyPage.RetentionInformation.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.RetentionInformation.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.RetentionInformation.Text2' | translate }}</p>
|
||||
|
||||
<h2 id="do-not-track-signals">
|
||||
{{ 'PrivacyPolicyPage.DoNotTrack.Title' | translate }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ 'PrivacyPolicyPage.DoNotTrack.Text1' | translate
|
||||
}}<a
|
||||
href="https://www.internetcookies.com"
|
||||
target="_blank"
|
||||
ref="nofollow noreferrer noopener external"
|
||||
>internetcookies.com</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<h2 id="links-to-other-resources">
|
||||
{{ 'PrivacyPolicyPage.LinksResources.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.LinksResources.Text1' | translate }}</p>
|
||||
|
||||
<h2 id="information-security">
|
||||
{{ 'PrivacyPolicyPage.InformationSecurity.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.InformationSecurity.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.InformationSecurity.Text2' | translate }}/p></p>
|
||||
<p>{{ 'PrivacyPolicyPage.InformationSecurity.Text3' | translate }}</p>
|
||||
|
||||
<h2 id="data-breach">
|
||||
{{ 'PrivacyPolicyPage.DataBreach.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.DataBreach.Text1' | translate }}</p>
|
||||
|
||||
<h2 id="changes-and-amendments">
|
||||
{{ 'PrivacyPolicyPage.Amendments.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.Amendments.Text1' | translate }}</p>
|
||||
<p>{{ 'PrivacyPolicyPage.Amendments.Text2' | translate }}</p>
|
||||
|
||||
<h2 id="acceptance-of-this-policy">
|
||||
{{ 'PrivacyPolicyPage.Acceptance.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.Acceptance.Text1' | translate }}</p>
|
||||
|
||||
<h2 id="contacting-us">
|
||||
{{ 'PrivacyPolicyPage.ContactingUs.Title' | translate }}
|
||||
</h2>
|
||||
<p>{{ 'PrivacyPolicyPage.ContactingUs.Text1' | translate }}</p>
|
||||
<p>
|
||||
<a
|
||||
href="mailto:sandkasten3a@gmail.com"
|
||||
>sandkasten3a@gmail.com</a
|
||||
>
|
||||
</p>
|
||||
<p>{{ 'PrivacyPolicyPage.ContactingUs.Text2' | translate }}</p>
|
||||
|
||||
<p>{{ 'PrivacyPolicyPage.Date' | translate }}</p>
|
||||
</body>
|
@ -1,3 +0,0 @@
|
||||
body {
|
||||
margin: 0 150px 0 150px;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PrivacyPolicyComponent } from './privacy-policy.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('PrivacyPolicyComponent', () => {
|
||||
let component: PrivacyPolicyComponent;
|
||||
let fixture: ComponentFixture<PrivacyPolicyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PrivacyPolicyComponent, TranslateModule.forRoot()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PrivacyPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-privacy-policy',
|
||||
templateUrl: './privacy-policy.component.html',
|
||||
styleUrl: './privacy-policy.component.scss',
|
||||
standalone: true,
|
||||
imports: [TranslateModule],
|
||||
})
|
||||
export class PrivacyPolicyComponent {
|
||||
constructor(private router: Router) {}
|
||||
|
||||
// Si click sur "Run", on redirige vers la page de politique de confidentialité
|
||||
onContinue(): void {
|
||||
this.router.navigateByUrl('/privacy-policy');
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
.form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
color: green;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
margin-top: 20px;
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<form [formGroup]="registerForm" (ngSubmit)="register()">
|
||||
<h1>Formulaire d'inscription :</h1>
|
||||
|
||||
<mat-form-field class="form-field">
|
||||
<mat-label>Enter your email</mat-label>
|
||||
<input
|
||||
matInput
|
||||
placeholder="pat@example.com"
|
||||
formControlName="email"
|
||||
required />
|
||||
<mat-error *ngIf="registerForm.controls.email.invalid">{{
|
||||
errorMessage
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="form-field">
|
||||
<mat-label>Enter your login</mat-label>
|
||||
<input matInput placeholder="pat" formControlName="login" required />
|
||||
<mat-error *ngIf="registerForm.controls.login.invalid">{{
|
||||
errorMessage
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="form-field">
|
||||
<mat-label>Enter your password</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[type]="hide ? 'password' : 'text'"
|
||||
formControlName="password"
|
||||
required />
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
(click)="hide = !hide"
|
||||
[attr.aria-label]="'Hide password'"
|
||||
[attr.aria-pressed]="hide">
|
||||
<mat-icon>{{ hide ? 'visibility_off' : 'visibility' }}</mat-icon>
|
||||
</button>
|
||||
<mat-error *ngIf="registerForm.controls.password.invalid">{{
|
||||
errorMessage
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-flat-button color="primary" type="submit">Créer un compte</button>
|
||||
|
||||
<!-- Message de retour de l'inscription -->
|
||||
<div *ngIf="successRegister" class="success-message">
|
||||
{{ successRegister }}
|
||||
</div>
|
||||
<div *ngIf="errorRegister" class="error-message">
|
||||
{{ errorRegister }}
|
||||
</div>
|
||||
</form>
|
@ -1,75 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import {
|
||||
FormControl,
|
||||
Validators,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
NgForm,
|
||||
FormBuilder,
|
||||
} from '@angular/forms';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { merge } from 'rxjs';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { UserService } from 'src/app/services/user.service';
|
||||
import { User } from 'src/app/models/user.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth',
|
||||
templateUrl: './register.component.html',
|
||||
styleUrl: './register.component.css',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
CommonModule,
|
||||
],
|
||||
})
|
||||
export class RegisterComponent {
|
||||
hide = true;
|
||||
|
||||
registerForm = this.formBuilder.group({
|
||||
email: ['', Validators.required],
|
||||
login: ['', Validators.required],
|
||||
password: ['', Validators.required],
|
||||
});
|
||||
errorMessage = '';
|
||||
|
||||
successRegister = '';
|
||||
errorRegister = '';
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private formBuilder: FormBuilder
|
||||
) {
|
||||
}
|
||||
|
||||
register() {
|
||||
const formRegisterValue = this.registerForm.value;
|
||||
|
||||
this.userService
|
||||
.postUser(
|
||||
formRegisterValue.email!,
|
||||
formRegisterValue.login!,
|
||||
formRegisterValue.password!
|
||||
)
|
||||
.subscribe((response) => {
|
||||
console.log('response :', response);
|
||||
if (response.success) {
|
||||
this.successRegister = 'Votre compte a été créé avec succès.';
|
||||
this.errorRegister = '';
|
||||
} else {
|
||||
this.errorRegister =
|
||||
"L'inscription a échoué : un compte avec ce login existe déjà.";
|
||||
this.successRegister = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<body>
|
||||
<h1>{{ 'TermsOfServicePage.Mentions' | translate }}</h1>
|
||||
|
||||
<p>{{ 'TermsOfServicePage.Law' | translate }}</p>
|
||||
<h2>{{ 'TermsOfServicePage.Edition' | translate }}</h2>
|
||||
<p>
|
||||
{{ 'TermsOfServicePage.URL' | translate }}
|
||||
<a href="www.sandkasten.fr">www.sandkasten.fr</a>
|
||||
{{ 'TermsOfServicePage.Site' | translate }}
|
||||
</p>
|
||||
<p>{{ 'TermsOfServicePage.Address' | translate }}</p>
|
||||
<h2>{{ 'TermsOfServicePage.Hosting' | translate }}</h2>
|
||||
<p>
|
||||
{{ 'TermsOfServicePage.Host' | translate
|
||||
}}<a href="https://www.hostinger.fr/contact"
|
||||
>https://www.hostinger.fr/contact</a
|
||||
>).
|
||||
</p>
|
||||
|
||||
<h2>{{ 'TermsOfServicePage.Publishing' | translate }}</h2>
|
||||
<p>{{ 'TermsOfServicePage.DirectorPublish' | translate }}</p>
|
||||
|
||||
<h2>{{ 'TermsOfServicePage.ContactUs' | translate }}</h2>
|
||||
<p>{{ 'TermsOfServicePage.Phone' | translate }}</p>
|
||||
<p>{{ 'TermsOfServicePage.Mail' | translate }}</p>
|
||||
<p>{{ 'TermsOfServicePage.Post' | translate }}</p>
|
||||
|
||||
<h2>{{ 'TermsOfServicePage.Data' | translate }}</h2>
|
||||
<p>{{ 'TermsOfServicePage.GDPR' | translate }}</p>
|
||||
</body>
|
@ -1,3 +0,0 @@
|
||||
body {
|
||||
margin: 0 150px 0 150px;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TermsOfServiceComponent } from './terms-of-service.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('TermsOfServiceComponent', () => {
|
||||
let component: TermsOfServiceComponent;
|
||||
let fixture: ComponentFixture<TermsOfServiceComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TermsOfServiceComponent, TranslateModule.forRoot()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TermsOfServiceComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-terms-of-service',
|
||||
templateUrl: './terms-of-service.component.html',
|
||||
styleUrl: './terms-of-service.component.scss',
|
||||
standalone: true,
|
||||
imports: [TranslateModule],
|
||||
})
|
||||
export class TermsOfServiceComponent {
|
||||
constructor(private router: Router) {}
|
||||
|
||||
// Si click sur "Run", on redirige vers la page de termes de service
|
||||
onContinue(): void {
|
||||
this.router.navigateByUrl('/terms-of-service');
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<div class="work-list-detail">
|
||||
<h4 class="work-list-detail--title">{{ work?.title }}</h4>
|
||||
<span class="work-list-detail--content">{{
|
||||
work?.content | slice: 0 : 50
|
||||
}}</span>
|
||||
<button class="work-list-detail--btn" [routerLink]="['/work/', work?.link]">
|
||||
Edit Code
|
||||
</button>
|
||||
</div>
|
@ -1,26 +0,0 @@
|
||||
.work-list-detail {
|
||||
background: lightgray;
|
||||
width: fit-content;
|
||||
padding: 2rem;
|
||||
border-radius: 1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
&--title {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&--content {
|
||||
}
|
||||
|
||||
&--btn {
|
||||
width: fit-content;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid black;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { WorkListDetailComponent } from './work-list-detail.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
describe('WorkListDetailComponent', () => {
|
||||
let component: WorkListDetailComponent;
|
||||
let fixture: ComponentFixture<WorkListDetailComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [WorkListDetailComponent, HttpClientModule, RouterModule.forRoot([])],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorkListDetailComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Work } from '../../models/work.model';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { SlicePipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-work-list-detail',
|
||||
standalone: true,
|
||||
imports: [RouterLink, SlicePipe],
|
||||
templateUrl: './work-list-detail.component.html',
|
||||
styleUrl: './work-list-detail.component.scss',
|
||||
})
|
||||
export class WorkListDetailComponent {
|
||||
@Input() work?: Work;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<div style="margin-left: 30px">
|
||||
<h2>Works</h2>
|
||||
|
||||
<h3>Last Work</h3>
|
||||
<div *ngFor="let work of works | slice: -1">
|
||||
<app-work-list-detail [work]="work"></app-work-list-detail>
|
||||
</div>
|
||||
|
||||
<h3>All Works</h3>
|
||||
<div class="all-works">
|
||||
<div *ngFor="let work of works">
|
||||
<app-work-list-detail [work]="work"></app-work-list-detail>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +0,0 @@
|
||||
.all-works {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WorksListComponent } from './works-list.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
describe('WorksListComponent', () => {
|
||||
let component: WorksListComponent;
|
||||
let fixture: ComponentFixture<WorksListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [WorksListComponent, HttpClientModule, RouterModule.forRoot([])],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorksListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Work } from '../../models/work.model';
|
||||
import { WorkService } from '../../services/work.service';
|
||||
import { NgForOf, SlicePipe } from '@angular/common';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { WorkListDetailComponent } from '../work-list-detail/work-list-detail.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-works-list',
|
||||
standalone: true,
|
||||
imports: [NgForOf, FormsModule, SlicePipe, WorkListDetailComponent],
|
||||
templateUrl: './works-list.component.html',
|
||||
styleUrl: './works-list.component.scss',
|
||||
})
|
||||
export class WorksListComponent implements OnInit {
|
||||
works: Work[] = [];
|
||||
|
||||
// TODO - REMOVE WHEN USER MANAGEMENT DONE
|
||||
FAKE_USER_ID = 1;
|
||||
|
||||
constructor(protected workService: WorkService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.workService.getWorks().then((works: Work[]) => {
|
||||
works.map((work: Work) => {
|
||||
if (work.user_id === this.FAKE_USER_ID) {
|
||||
this.works.push(work);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(form: NgForm) {
|
||||
this.workService.saveWork(form);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
.form-group {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<form action="" class="form-group">
|
||||
<mat-form-field appearence="fill">
|
||||
<mat-label>Nom</mat-label>
|
||||
<input matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearence="fill">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearence="fill">
|
||||
<mat-label>Type</mat-label>
|
||||
<mat-select>
|
||||
<mat-option value="Bug">Bug</mat-option>
|
||||
<mat-option value="Recommendation">Recommendation</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearence="fill">
|
||||
<mat-label>Message</mat-label>
|
||||
<textarea matInput></textarea>
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="primary">Envoyer message</button>
|
||||
</form>
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContactComponent } from './contact.component';
|
||||
|
||||
describe('ContactComponent', () => {
|
||||
let component: ContactComponent;
|
||||
let fixture: ComponentFixture<ContactComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ContactComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(ContactComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,28 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact',
|
||||
templateUrl: './contact.component.html',
|
||||
styleUrls: ['./contact.component.css']
|
||||
})
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
MatFormFieldModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
ReactiveFormsModule
|
||||
]
|
||||
})
|
||||
|
||||
export class ContactComponent {
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
|
||||
.love-hugo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
font-size: 3rem;
|
||||
color: pink;
|
||||
text-align: center;
|
||||
}
|
@ -0,0 +1 @@
|
||||
<span class="love-hugo">Ceci est la page Documentation réclamé par Hugo le 21 Septembre 2023. Elle contient une documentation inconnue à ce jour. Cepandant, elle est très importante pour le bon fonctionnement du site et la compréhension de toutes les fonctionnalités ajoutées par l'équipe Sandkasten</span>
|
@ -0,0 +1,9 @@
|
||||
.code_input {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.code_input .code_input_textarea {
|
||||
background-color: #cccccc;
|
||||
min-height: 80vh;
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<div class="code_input">
|
||||
<textarea
|
||||
class="code_input_textarea"
|
||||
id="code_input"
|
||||
name="code_input"
|
||||
placeholder="Enter your code here..."
|
||||
></textarea>
|
||||
<button (click)="onRunButtonClicked()" [disabled]="isLoaded">Run</button>
|
||||
<div *ngIf="!isLoaded">
|
||||
<p>Loading: {{ loadingProgress }}%</p>
|
||||
<div class="loading-bar" [style.width.%]="loadingProgress"></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,35 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-editor',
|
||||
templateUrl: './editor.component.html',
|
||||
styleUrls: ['./editor.component.css']
|
||||
})
|
||||
export class EditorComponent implements OnInit{
|
||||
loadingProgress: number = 0; // Pour suivre la progression du chargement
|
||||
isLoaded: boolean = false; // Pour vérifier si le chargement est terminé
|
||||
|
||||
constructor(private router: Router){}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
// Si click sur "Run", on redirige vers la page d'output
|
||||
onRunButtonClicked() {
|
||||
this.loadingProgress = 0;
|
||||
this.isLoaded = false;
|
||||
|
||||
// Simuler le chargement pendant 5 secondes
|
||||
const interval = setInterval(() => {
|
||||
this.loadingProgress++;
|
||||
if (this.loadingProgress >= 100) {
|
||||
clearInterval(interval); // Arrêtez la simulation de chargement
|
||||
this.isLoaded = true;
|
||||
// Redirigez vers une autre page (par exemple, 'output')
|
||||
this.router.navigate(['/output']);
|
||||
}
|
||||
}, 50); // Augmenter la valeur pour ralentir la progression du chargement si nécessaire
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
|
||||
.footer {
|
||||
background-color: #494b92;
|
||||
padding: .5rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.sandkasten-logo {
|
||||
width: 4rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
<div class="footer" >
|
||||
|
||||
<img class="sandkasten-logo"
|
||||
routerLink=""
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[src]="sandkasten_logo" alt="Logo-Sandkasten" />
|
||||
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.css']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
sandkasten_logo!: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.sandkasten_logo = 'assets/img/logo.png';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
|
||||
.toolbar {
|
||||
background-color: #494b92;
|
||||
padding: .5rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.left_part {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.left_part .logo_container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.left_part .menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left_part .menu a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.left_part .menu a:hover {
|
||||
color: yellow;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.left_part .menu a.active {
|
||||
color: yellow;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.sandkasten-logo {
|
||||
width: 4rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.logo_title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.right_part {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gitea_logo_container .gitea-logo {
|
||||
width: 4rem;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.toolbar {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.toolbar .left_part {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.toolbar .left_part .logo_container .logo_title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
<div class="toolbar" role="banner">
|
||||
|
||||
<div class="left_part">
|
||||
<div class="logo_container">
|
||||
<img class="sandkasten-logo"
|
||||
routerLink=""
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[src]="sandkasten_logo" alt="Logo-Sandkasten" />
|
||||
<h2 class="logo_title">{{ title }}</h2>
|
||||
</div>
|
||||
|
||||
|
||||
<nav class="menu">
|
||||
<a
|
||||
routerLink=""
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>Accueil</a>
|
||||
|
||||
<a
|
||||
routerLink="editor"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>Editeur</a>
|
||||
|
||||
<a
|
||||
routerLink="documentation"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>Documentation</a>
|
||||
|
||||
<a routerLink="contact" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Contact</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="right_part">
|
||||
<a href="https://codefirst.iut.uca.fr/git/sandkasten"
|
||||
class="gitea_logo_container">
|
||||
<img class="gitea-logo"
|
||||
[src]="gitea_logo" alt="Logo-Gitea" />
|
||||
</a>
|
||||
<p>Version : {{ version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
|
||||
import {Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.css']
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
title!: string;
|
||||
version!: string;
|
||||
sandkasten_logo!: string;
|
||||
gitea_logo!: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.title = 'Sandkasten';
|
||||
this.version = '1.0';
|
||||
this.sandkasten_logo = 'assets/img/logo.png';
|
||||
this.gitea_logo = 'assets/img/gitea.png';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// export class ResponsiveComponent implements OnInit {
|
||||
//
|
||||
// isPhonePortrait = false;
|
||||
//
|
||||
// constructor(private responsive: BreakpointObserver) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// ngOnInit() {
|
||||
//
|
||||
// this.responsive.observe(Breakpoints.HandsetPortrait)
|
||||
// .subscribe(result => {
|
||||
//
|
||||
// this.isPhonePortrait = false;
|
||||
//
|
||||
// if (result.matches) {
|
||||
// this.isPhonePortrait = true;
|
||||
// }
|
||||
//
|
||||
// });
|
||||
//
|
||||
// }
|
||||
// }
|
@ -0,0 +1,9 @@
|
||||
|
||||
.landing-links {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button{
|
||||
font-size: 20px;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<div class="landing-block">
|
||||
<div class="hero-container">
|
||||
<h2 class="hero-title">Bienvenue sur Sandkasten !</h2>
|
||||
<span class="hero-text">Sandkasten est un site vous permettant de tester votre code.</span>
|
||||
</div>
|
||||
|
||||
<div class="landing-links">
|
||||
<button (click)="onContinue()">Try it now !</button>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -0,0 +1,18 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-landing-page',
|
||||
templateUrl: './landing-page.component.html',
|
||||
styleUrls: ['./landing-page.component.css']
|
||||
})
|
||||
export class LandingPageComponent implements OnInit{
|
||||
constructor(private router: Router){}
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
// Si click sur "Run", on redirige vers la page d'édition
|
||||
onContinue():void{
|
||||
this.router.navigateByUrl('/editor');
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export interface User {
|
||||
id_user: number;
|
||||
login: string;
|
||||
password: string;
|
||||
permissions: number;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
export interface Work {
|
||||
id_work: number;
|
||||
link: string;
|
||||
user_id: number;
|
||||
language: string;
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
@ -0,0 +1 @@
|
||||
<p>output works!</p>
|
@ -1,16 +1,16 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FormComponent } from './form.component';
|
||||
import { OutputComponent } from './output.component';
|
||||
|
||||
describe('FormComponent', () => {
|
||||
let component: FormComponent;
|
||||
let fixture: ComponentFixture<FormComponent>;
|
||||
describe('OutputComponent', () => {
|
||||
let component: OutputComponent;
|
||||
let fixture: ComponentFixture<OutputComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormComponent],
|
||||
declarations: [OutputComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(FormComponent);
|
||||
fixture = TestBed.createComponent(OutputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-output',
|
||||
templateUrl: './output.component.html',
|
||||
styleUrls: ['./output.component.css']
|
||||
})
|
||||
export class OutputComponent {
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { SafeHTMLPipe } from './safe-html.pipe';
|
||||
import { inject } from '@angular/core/testing';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
describe('SafeHTMLPipe', () => {
|
||||
it('create an instance', inject(
|
||||
[DomSanitizer],
|
||||
(domSanitizer: DomSanitizer) => {
|
||||
const pipe = new SafeHTMLPipe(domSanitizer);
|
||||
expect(pipe).toBeTruthy();
|
||||
}
|
||||
));
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({
|
||||
name: 'safeHTML',
|
||||
standalone: true,
|
||||
})
|
||||
export class SafeHTMLPipe implements PipeTransform {
|
||||
constructor(protected sanitizer: DomSanitizer) {}
|
||||
|
||||
transform(value: unknown): unknown {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(value as string);
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { SSE } from 'sse.js';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export type ExecutionMessage = {
|
||||
type: 'stdout' | 'stderr' | 'exit';
|
||||
text: string;
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class BackendService {
|
||||
private apiUrl = environment.apiUrl;
|
||||
|
||||
private resultSubject = new Subject<ExecutionMessage>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
async createRoom(code: string) {
|
||||
const reponse = await fetch(`${this.apiUrl}/live`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ code }),
|
||||
});
|
||||
return reponse.text();
|
||||
}
|
||||
|
||||
executeCode(code: string, language: string) {
|
||||
const sse = new SSE(`${this.apiUrl}/run`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'text/event-stream',
|
||||
},
|
||||
payload: JSON.stringify({ code, language }),
|
||||
});
|
||||
|
||||
sse.addEventListener('message', (event: MessageEvent<string>) => {
|
||||
const result = event.data;
|
||||
|
||||
// @ts-expect-error The type is not declared although present
|
||||
const type = event.id;
|
||||
const text = decodeURIComponent(result.replace(/%00/g, ''));
|
||||
if (type === 'end') {
|
||||
sse.close();
|
||||
}
|
||||
this.resultSubject.next({ type, text });
|
||||
});
|
||||
}
|
||||
|
||||
getResult(): Observable<ExecutionMessage> {
|
||||
return this.resultSubject.asObservable();
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import { ChangeSet, Text } from '@codemirror/state';
|
||||
import { EditorView } from 'codemirror';
|
||||
import {
|
||||
Update,
|
||||
collab,
|
||||
getSyncedVersion,
|
||||
receiveUpdates,
|
||||
sendableUpdates,
|
||||
} from '@codemirror/collab';
|
||||
import { ViewPlugin, ViewUpdate } from '@codemirror/view';
|
||||
|
||||
export class Connection {
|
||||
private requestId = 0;
|
||||
private resolves: Record<number, (value: any) => void> = {};
|
||||
|
||||
constructor(private client: WebSocket) {
|
||||
client.addEventListener('message', (event) => {
|
||||
const response = JSON.parse(event.data);
|
||||
if ('_request' in response) {
|
||||
const resolve = this.resolves[response._request];
|
||||
if (resolve) {
|
||||
resolve(response.payload);
|
||||
} else {
|
||||
console.error(
|
||||
'Received response for unknown or already used request',
|
||||
response._request
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error('Received invalid response', response._request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
request(body: Record<string, unknown>): Promise<any> {
|
||||
body['_request'] = this.requestId;
|
||||
this.client.send(JSON.stringify(body));
|
||||
return new Promise(
|
||||
(resolve) => (this.resolves[this.requestId++] = resolve)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function pushUpdates(
|
||||
connection: Connection,
|
||||
version: number,
|
||||
fullUpdates: readonly Update[]
|
||||
): Promise<boolean> {
|
||||
// Strip off transaction data
|
||||
let updates = fullUpdates.map((u) => ({
|
||||
clientID: u.clientID,
|
||||
changes: u.changes.toJSON(),
|
||||
}));
|
||||
return connection.request({ type: 'pushUpdates', version, updates });
|
||||
}
|
||||
|
||||
function pullUpdates(
|
||||
connection: Connection,
|
||||
version: number
|
||||
): Promise<readonly Update[]> {
|
||||
return connection.request({ type: 'pullUpdates', version }).then((updates) =>
|
||||
updates.map((u: any) => ({
|
||||
changes: ChangeSet.fromJSON(u.changes),
|
||||
clientID: u.clientID,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
export function getDocument(
|
||||
connection: Connection
|
||||
): Promise<{ version: number; doc: Text }> {
|
||||
return connection.request({ type: 'getDocument' }).then((data) => ({
|
||||
version: data.version,
|
||||
doc: Text.of(data.doc.split('\n')),
|
||||
}));
|
||||
}
|
||||
|
||||
export function peerExtension(startVersion: number, connection: Connection) {
|
||||
let plugin = ViewPlugin.fromClass(
|
||||
class {
|
||||
private pushing = false;
|
||||
private done = false;
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
this.pull();
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (update.docChanged) this.push();
|
||||
}
|
||||
|
||||
async push() {
|
||||
let updates = sendableUpdates(this.view.state);
|
||||
|
||||
if (this.pushing || !updates.length) return;
|
||||
this.pushing = true;
|
||||
let version = getSyncedVersion(this.view.state);
|
||||
await pushUpdates(connection, version, updates);
|
||||
this.pushing = false;
|
||||
// Regardless of whether the push failed or new updates came in
|
||||
// while it was running, try again if there's updates remaining
|
||||
if (sendableUpdates(this.view.state).length)
|
||||
setTimeout(() => this.push(), 100);
|
||||
}
|
||||
|
||||
async pull() {
|
||||
while (!this.done) {
|
||||
let version = getSyncedVersion(this.view.state);
|
||||
let updates = await pullUpdates(connection, version);
|
||||
this.view.dispatch(receiveUpdates(this.view.state, updates));
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.done = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
return [collab({ startVersion }), plugin];
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ThemeService {
|
||||
private _darkTheme = new BehaviorSubject<boolean>(false);
|
||||
isDarkTheme = this._darkTheme.asObservable();
|
||||
|
||||
toggleDarkTheme() {
|
||||
this._darkTheme.next(!this._darkTheme.value);
|
||||
console.log('Theme toggled');
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TranslationService {
|
||||
get language() {
|
||||
return this.translate.currentLang;
|
||||
}
|
||||
|
||||
constructor(private translate: TranslateService) {}
|
||||
|
||||
onLanguageChange(language: 'fr' | 'en') {
|
||||
this.translate.use(language);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
postUser(
|
||||
email: string,
|
||||
login: string,
|
||||
password: string
|
||||
): Observable<Response> {
|
||||
const body = {
|
||||
email: email,
|
||||
login: login,
|
||||
password: password,
|
||||
permissions: 0,
|
||||
};
|
||||
|
||||
return this.http.post<Response>(`${environment.apiUrl}/users`, body);
|
||||
}
|
||||
|
||||
loginUser(login: string, password: string): Promise<Response> {
|
||||
const body = {
|
||||
login: login,
|
||||
password: password,
|
||||
};
|
||||
|
||||
return fetch(`${environment.apiUrl}/users/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: 'include',
|
||||
}).then((response) => response.json());
|
||||
}
|
||||
|
||||
logoutUser(): Observable<Response> {
|
||||
return this.http.post<Response>(`${environment.apiUrl}/users/logout`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type Response = {
|
||||
success: boolean;
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WorkService } from './work.service';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
describe('WorkService', () => {
|
||||
let service: WorkService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientModule]
|
||||
});
|
||||
service = TestBed.inject(WorkService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,48 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Work } from '../models/work.model';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class WorkService {
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getWorks(): Promise<Work[]> {
|
||||
return fetch(`${environment.apiUrl}/works`, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
}).then((response) => response.json());
|
||||
}
|
||||
|
||||
getWorkByLink(link: string): Observable<Work | null> {
|
||||
return this.http.get<Work>(`${environment.apiUrl}/works/${link}`);
|
||||
}
|
||||
|
||||
saveWork(form: NgForm): void {
|
||||
const code = form.value.content;
|
||||
|
||||
this.http.post(`${environment.apiUrl}/works/save`, code, { withCredentials: true });
|
||||
}
|
||||
|
||||
postWork(code: string, language: string): Observable<string> {
|
||||
const body = {
|
||||
language,
|
||||
title: `Basic ${language}`,
|
||||
code,
|
||||
};
|
||||
|
||||
return this.http.post<Work>(`${environment.apiUrl}/works`, body).pipe(map((work) => work.link));
|
||||
}
|
||||
|
||||
updateWork(id: string, code: string, language: string): void {
|
||||
const body = {
|
||||
newContent: code,
|
||||
language: language,
|
||||
};
|
||||
this.http.put(`${environment.apiUrl}/works/${id}/content`, body, { withCredentials: true });
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
{
|
||||
"LandingPage": {
|
||||
"Welcome": "Welcome to Sandkasten",
|
||||
"Description": "You've arrived at the best code testing sandbox site! We let you test all your programs in your favorite languages. Thanks to the Code Mirror editor and our personalized management of execution on containers, your code becomes completely harmless, so you can have as much fun as you like!",
|
||||
"Try": "Try it now !",
|
||||
"Bouton1": "Code editor",
|
||||
"Bouton2": "Git repository",
|
||||
"Top": {
|
||||
"Tittle": "What you need to know about our editor",
|
||||
"Description": "Sandkasten, the editor with all the options you need. With these advantages, you can test your code and see the results."
|
||||
},
|
||||
"List": {
|
||||
"Tittle1": "Security",
|
||||
"Text1": "To guarantee the robustness of our tool, we run your code in a closed environment, limiting costly requests. This protects us from attempts to misuse the system.",
|
||||
"Tittle2": "Import / Export",
|
||||
"Text2": "You can import files and the editor will recognize the language by its extension. You can also export the code you've made with just one click.",
|
||||
"Tittle3": "Options",
|
||||
"Text3": "You have numerous options for customizing and optimizing your use of Sandkasten. These options are available directly from the editor.",
|
||||
"Tittle4": "Multi-langues",
|
||||
"Text4": "Sandkasten supports a wide range of code languages. When you type code or import a file, the editor automatically recognizes the language. You can also select it directly in the editor.",
|
||||
"Tittle5": "Lorem Ipsum",
|
||||
"Text5": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
|
||||
"Tittle6": "Lorem Ipsum",
|
||||
"Text6": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam"
|
||||
},
|
||||
"Technologie": {
|
||||
"Tittle": "Languages disponibles dès maintenant"
|
||||
},
|
||||
"About": {
|
||||
"Tittle": "Empowering the world with Sandkasten.",
|
||||
"Text": "We're a team professionally engaged in the world of technology! We bring different personalities, experiences and skills to the table. That's what makes our team so special."
|
||||
},
|
||||
"MemberRole": "Developper",
|
||||
"SocialTittle": "Join us on our networks",
|
||||
"GetStarted": {
|
||||
"Tittle": "Try Sandkasten",
|
||||
"Description": "Join the Sandkasten community now by testing our editor!",
|
||||
"Button": "Get Started !"
|
||||
}
|
||||
},
|
||||
"HeaderPage": {
|
||||
"Work": "Work",
|
||||
"Editor": "Editor",
|
||||
"Documentation": "Documentation",
|
||||
"Contact": "Contact us"
|
||||
},
|
||||
"FooterPage": {
|
||||
"Legal": "Legal mentions",
|
||||
"Terms": "Terms of use",
|
||||
"Privacy": "Privacy policy",
|
||||
"Rights": "Copyright © 2024 Sandkasten. All rights reserved."
|
||||
},
|
||||
"TermsOfServicePage": {
|
||||
"Mentions": "LEGALS MENTIONS",
|
||||
"Law": "In accordance with the provisions of Law No. 2004-575 of June 21, 2004, relating to confidence in the digital economy, the identity of the various contributors involved in the creation and monitoring of the Sandkasten website is specified to users.",
|
||||
"Edition": "Website editing.",
|
||||
"URL": "The present website, accessible at the URL",
|
||||
"Site": "(the « Site »), is edited by:",
|
||||
"Address": "Sandkasten, a company with capital of 100 euros, registered with the R.C.S. of CLERMONT-FERRAND under the number RCS CLERMONT-FERRAND B 123 456 789, whose registered office is located at 7 Av. Blaise Pascal 63170 Aubière, represented by Jul Lamiff, duly authorized.",
|
||||
"Hosting": "Hosting",
|
||||
"Host": "The Site is hosted by Hostinger International LTD, located in , (telephone contact or email :",
|
||||
"Publishing": "Publishing Director",
|
||||
"DirectorPublish": "The Site's Publishing Director is Jul Lamiff.",
|
||||
"ContactUs": "Contact us",
|
||||
"Phone": "By phone: +33651107802",
|
||||
"Mail": "By email: sandkasten3a@gmail.com",
|
||||
"Post": "By post: 7 Av. Blaise Pascal 63170 Aubière",
|
||||
"Data": "Personal data",
|
||||
"GDPR": "The processing of your personal data is governed by our Privacy Charter, available from the « Personal Data Protection Charter » section, in accordance with the General Data Protection Regulation 2016/679 of April 27, 2016 («GDPR»)."
|
||||
},
|
||||
"PrivacyPolicyPage": {
|
||||
"PrivacyPolicy": "Privacy Policy",
|
||||
"Promise": "We respect your privacy and are committed to protecting it through our compliance with this privacy policy (« Policy »). This Policy describes the types of information we may collect from you or that you may provide (« Personal Information ») on the ",
|
||||
"PromisePart2": "website (« Website » or « Service ») and any of its related products and services (collectively, « Services »), and our practices for collecting, using, maintaining, protecting, and disclosing that Personal Information. It also describes the choices available to you regarding our use of your Personal Information and how you can access and update it.",
|
||||
"Policy": "This Policy is a legally binding agreement between you (« User », « you » or « your ») and Sandkasten Inc. (« Sandkasten Inc. », « we », « us » or « our »). If you are entering into this Policy on behalf of a business or other legal entity, you represent that you have the authority to bind such entity to this Policy, in which case the terms « User », « you » or « your » shall refer to such entity. If you do not have such authority, or if you do not agree with the terms of this Policy, you must not accept this Policy and may not access and use the Website and Services. By accessing and using the Website and Services, you acknowledge that you have read, understood, and agree to be bound by the terms of this Policy. This Policy does not apply to the practices of companies that we do not own or control, or to individuals that we do not employ or manage.",
|
||||
"TableContents": "Table of contents",
|
||||
"PersonalInformation": {
|
||||
"Title": "Collection of personal information",
|
||||
"Text1": "You can access and use the Website and Services without telling us who you are or revealing any information by which someone could identify you as a specific, identifiable individual. If, however, you wish to use some of the features offered on the Website, you may be asked to provide certain Personal Information (for example, your name and e-mail address).",
|
||||
"Text2": "We receive and store any information you knowingly provide to us when you create an account, or fill any forms on the Website. When required, this information may include the following: ",
|
||||
"Text3": "Account details (such as user name, unique user ID, password, etc)",
|
||||
"Text4": "Contact information (such as email address, phone number, etc)",
|
||||
"Text5": "You can choose not to provide us with your Personal Information, but then you may not be able to take advantage of some of the features on the Website. Users who are uncertain about what information is mandatory are welcome to contact us."
|
||||
},
|
||||
"PrivacyChildren": {
|
||||
"Title": "Privacy of children",
|
||||
"Text1": "We do not knowingly collect any Personal Information from children under the age of 13. If you are under the age of 13, please do not submit any Personal Information through the Website and Services. If you have reason to believe that a child under the age of 13 has provided Personal Information to us through the Website and Services, please contact us to request that we delete that child's Personal Information from our Services.",
|
||||
"Text2": "We encourage parents and legal guardians to monitor their children's Internet usage and to help enforce this Policy by instructing their children never to provide Personal Information through the Website and Services without their permission. We also ask that all parents and legal guardians overseeing the care of children take the necessary precautions to ensure that their children are instructed to never give out Personal Information when online without their permission."
|
||||
},
|
||||
"UseProcInformation": {
|
||||
"Title": "Use and processing of collected information",
|
||||
"Text1": "We act as a data controller and a data processor when handling Personal Information, unless we have entered into a data processing agreement with you in which case you would be the data controller and we would be the data processor.",
|
||||
"Text2": "Our role may also differ depending on the specific situation involving Personal Information. We act in the capacity of a data controller when we ask you to submit your Personal Information that is necessary to ensure your access and use of the Website and Services. In such instances, we are a data controller because we determine the purposes and means of the processing of Personal Information.",
|
||||
"Text3": "We act in the capacity of a data processor in situations when you submit Personal Information through the Website and Services. We do not own, control, or make decisions about the submitted Personal Information, and such Personal Information is processed only in accordance with your instructions. In such instances, the User providing Personal Information acts as a data controller.",
|
||||
"Text4": "In order to make the Website and Services available to you, or to meet a legal obligation, we may need to collect and use certain Personal Information. If you do not provide the information that we request, we may not be able to provide you with the requested products or services. Any of the information we collect from you may be used for the following purposes:",
|
||||
"List1": "Create and manage user accounts",
|
||||
"List2": "Request user feedback",
|
||||
"List3": "Respond to legal requests and prevent harm",
|
||||
"List4": "Run and operate the Website and Services",
|
||||
"Text5": "Processing your Personal Information depends on how you interact with the Website and Services, where you are located in the world and if one of the following applies: (i) you have given your consent for one or more specific purposes; (ii) provision of information is necessary for the performance of this Policy with you and/or for any pre-contractual obligations thereof; (iii) processing is necessary for compliance with a legal obligation to which you are subject; (iv) processing is related to a task that is carried out in the public interest or in the exercise of official authority vested in us; (v) processing is necessary for the purposes of the legitimate interests pursued by us or by a third party.",
|
||||
"Text6": "Note that under some legislations we may be allowed to process information until you object to such processing by opting out, without having to rely on consent or any other of the legal bases. In any case, we will be happy to clarify the specific legal basis that applies to the processing, and in particular whether the provision of Personal Information is a statutory or contractual requirement, or a requirement necessary to enter into a contract. « ManagingInformation »: « Managing information »"
|
||||
},
|
||||
"ManagingInformation": {
|
||||
"Title": "Managing information",
|
||||
"Text1": "You are able to delete certain Personal Information we have about you. The Personal Information you can delete may change as the Website and Services change. When you delete Personal Information, however, we may maintain a copy of the unrevised Personal Information in our records for the duration necessary to comply with our obligations to our affiliates and partners, and for the purposes described below."
|
||||
},
|
||||
"DisclosureInformation": {
|
||||
"Title": "Disclosure of information",
|
||||
"Text1": "To maintain the highest level of privacy and to protect your Personal Information to the full extent, we do not share your Personal Information with any third parties.",
|
||||
"Text2": "However, we may also disclose any Personal Information we collect, use or receive if required or permitted by law, such as to comply with a subpoena or similar legal process, and when we believe in good faith that disclosure is necessary to protect our rights, protect your safety or the safety of others, investigate fraud, or respond to a government request."
|
||||
},
|
||||
"RetentionInformation": {
|
||||
"Title": "Retention of information",
|
||||
"Text1": "We will retain and use your Personal Information for the period necessary as long as your user account remains active, to enforce our Policy, resolve disputes, and unless a longer retention period is required or permitted by law.",
|
||||
"Text2": "We may use any aggregated data derived from or incorporating your Personal Information after you update or delete it, but not in a manner that would identify you personally. Once the retention period expires, Personal Information shall be deleted. Therefore, the right to access, the right to erasure, the right to rectification, and the right to data portability cannot be enforced after the expiration of the retention period."
|
||||
},
|
||||
"DoNotTrack": {
|
||||
"Title": "Do Not Track signals",
|
||||
"Text1": "Some browsers incorporate a Do Not Track feature that signals to websites you visit that you do not want to have your online activity tracked. Tracking is not the same as using or collecting information in connection with a website. For these purposes, tracking refers to collecting personally identifiable information from consumers who use or visit a website or online service as they move across different websites over time. How browsers communicate the Do Not Track signal is not yet uniform. As a result, the Website and Services are not yet set up to interpret or respond to Do Not Track signals communicated by your browser. Even so, as described in more detail throughout this Policy, we limit our use and collection of your Personal Information. For a description of Do Not Track protocols for browsers and mobile devices or to learn more about the choices available to you, visit "
|
||||
},
|
||||
"LinksResources": {
|
||||
"Title": "Links to other resources",
|
||||
"Text1": "The Website and Services contain links to other resources that are not owned or controlled by us. Please be aware that we are not responsible for the privacy practices of such other resources or third parties. We encourage you to be aware when you leave the Website and Services and to read the privacy statements of each and every resource that may collect Personal Information."
|
||||
},
|
||||
"InformationSecurity": {
|
||||
"Title": "Information security",
|
||||
"Text1": "We secure information you provide on computer servers in a controlled, secure environment, protected from unauthorized access, use, or disclosure. We maintain reasonable administrative, technical, and physical safeguards in an effort to protect against unauthorized access, use, modification, and disclosure of Personal Information in our control and custody. However, no data transmission over the Internet or wireless network can be guaranteed.",
|
||||
"Text2": "Therefore, while we strive to protect your Personal Information, you acknowledge that (i) there are security and privacy limitations of the Internet which are beyond our control; (ii) the security, integrity, and privacy of any and all information and data exchanged between you and the Website and Services cannot be guaranteed; and (iii) any such information and data may be viewed or tampered with in transit by a third party, despite best efforts.",
|
||||
"Text3": "As the security of Personal Information depends in part on the security of the device you use to communicate with us and the security you use to protect your credentials, please take appropriate measures to protect this information."
|
||||
},
|
||||
"DataBreach": {
|
||||
"Title": "Data breach",
|
||||
"Text1": "In the event we become aware that the security of the Website and Services has been compromised or Users' Personal Information has been disclosed to unrelated third parties as a result of external activity, including, but not limited to, security attacks or fraud, we reserve the right to take reasonably appropriate measures, including, but not limited to, investigation and reporting, as well as notification to and cooperation with law enforcement authorities. In the event of a data breach, we will make reasonable efforts to notify affected individuals if we believe that there is a reasonable risk of harm to the User as a result of the breach or if notice is otherwise required by law. When we do, we will send you an email."
|
||||
},
|
||||
"Amendments": {
|
||||
"Title": "Changes and amendments",
|
||||
"Text1": "We reserve the right to modify this Policy or its terms related to the Website and Services at any time at our discretion. When we do, we will revise the updated date at the bottom of this page. We may also provide notice to you in other ways at our discretion, such as through the contact information you have provided.",
|
||||
"Text2": "An updated version of this Policy will be effective immediately upon the posting of the revised Policy unless otherwise specified. Your continued use of the Website and Services after the effective date of the revised Policy (or such other act specified at that time) will constitute your consent to those changes. However, we will not, without your consent, use your Personal Information in a manner materially different than what was stated at the time your Personal Information was collected."
|
||||
},
|
||||
"Acceptance": {
|
||||
"Title": "Acceptance of this policy",
|
||||
"Text1": "You acknowledge that you have read this Policy and agree to all its terms and conditions. By accessing and using the Website and Services and submitting your information you agree to be bound by this Policy. If you do not agree to abide by the terms of this Policy, you are not authorized to access or use the Website and Services."
|
||||
},
|
||||
"ContactingUs": {
|
||||
"Title": "Contacting us",
|
||||
"Text1": "If you have any questions, concerns, or complaints regarding this Policy, the information we hold about you, or if you wish to exercise your rights, we encourage you to contact us using the details below:",
|
||||
"Text2": "We will attempt to resolve complaints and disputes and make every reasonable effort to honor your wish to exercise your rights as quickly as possible and in any event, within the timescales provided by applicable data protection laws."
|
||||
},
|
||||
"Date": "This document was last updated on January 10, 2024"
|
||||
},
|
||||
"Editor": {
|
||||
"Share": "Share",
|
||||
"Save": "Save",
|
||||
"Run": "RUN",
|
||||
"Add": "ADD BDD"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 258 KiB |
Before Width: | Height: | Size: 902 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue