Compare commits

..

1 Commits

Author SHA1 Message Date
Clément FRÉVILLE 0e0804acdd Update to Angular 18 + ESLint 9
continuous-integration/drone/push Build is passing Details
1 year ago

@ -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": {}
}
]
}

@ -25,7 +25,6 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []
@ -41,8 +40,8 @@
"budgets": [ "budgets": [
{ {
"type": "initial", "type": "initial",
"maximumWarning": "2mb", "maximumWarning": "500kb",
"maximumError": "3mb" "maximumError": "1mb"
}, },
{ {
"type": "anyComponentStyle", "type": "anyComponentStyle",
@ -92,7 +91,6 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [], "scripts": [],

@ -0,0 +1,44 @@
// @ts-check
const eslint = require("@eslint/js");
const tseslint = require("typescript-eslint");
const angular = require("angular-eslint");
module.exports = tseslint.config(
{
files: ["**/*.ts"],
extends: [
eslint.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
...angular.configs.tsRecommended,
],
processor: angular.processInlineTemplates,
rules: {
"@angular-eslint/directive-selector": [
"error",
{
type: "attribute",
prefix: "app",
style: "camelCase",
},
],
"@angular-eslint/component-selector": [
"error",
{
type: "element",
prefix: "app",
style: "kebab-case",
},
],
"@typescript-eslint/no-inferrable-types": "off",
},
},
{
files: ["**/*.html"],
extends: [
...angular.configs.templateRecommended,
...angular.configs.templateAccessibility,
],
rules: {},
}
);

7517
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -12,47 +12,40 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^17.3.7", "@angular/animations": "^18.0.1",
"@angular/cdk": "^17.3.7", "@angular/cdk": "^18.0.3",
"@angular/common": "^17.3.7", "@angular/common": "^18.0.1",
"@angular/compiler": "^17.3.7", "@angular/compiler": "^18.0.1",
"@angular/core": "^17.3.7", "@angular/core": "^18.0.1",
"@angular/forms": "^17.3.7", "@angular/forms": "^18.0.3",
"@angular/material": "^17.3.8", "@angular/platform-browser": "^18.0.1",
"@angular/platform-browser": "^17.3.7", "@angular/platform-browser-dynamic": "^18.0.3",
"@angular/platform-browser-dynamic": "^17.3.7", "@angular/router": "^18.0.3",
"@angular/router": "^17.3.7",
"@codemirror/collab": "^6.1.1", "@codemirror/collab": "^6.1.1",
"@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-cpp": "^6.0.2",
"@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-javascript": "^6.2.2",
"@codemirror/language": "^6.10.2", "@codemirror/language": "^6.10.2",
"@codemirror/legacy-modes": "^6.4.0", "@codemirror/legacy-modes": "^6.4.0",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.26.3", "@codemirror/view": "^6.28.1",
"@emailjs/browser": "^4.3.3", "@emailjs/browser": "^4.3.3",
"@ngx-translate/core": "^15.0.0", "@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0", "@ngx-translate/http-loader": "^8.0.0",
"@sandkasten/codemirror6-editor": "^0.0.2", "@sandkasten/codemirror6-editor": "^0.0.3",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"rxjs": "~7.8.1", "rxjs": "~7.8.1",
"sse.js": "^2.4.1", "sse.js": "^2.5.0",
"tslib": "^2.6.2", "tslib": "^2.6.3",
"zone.js": "~0.14.5" "zone.js": "~0.14.7"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.3.7", "@angular-devkit/build-angular": "^18.0.4",
"@angular-eslint/builder": "^17.4.0", "@angular/cli": "~18.0.2",
"@angular-eslint/eslint-plugin": "^17.4.0", "@angular/compiler-cli": "^18.0.3",
"@angular-eslint/eslint-plugin-template": "^17.4.0",
"@angular-eslint/schematics": "^17.4.0",
"@angular-eslint/template-parser": "^17.4.0",
"@angular/cli": "~17.3.7",
"@angular/compiler-cli": "^17.3.7",
"@types/jasmine": "~5.1.4", "@types/jasmine": "~5.1.4",
"@typescript-eslint/eslint-plugin": "^7.9.0", "angular-eslint": "18.0.1",
"@typescript-eslint/parser": "^7.9.0", "eslint": "^9.5.0",
"eslint": "^8.57.0",
"jasmine-core": "~5.1.2", "jasmine-core": "~5.1.2",
"karma": "~6.4.2", "karma": "~6.4.2",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
@ -60,7 +53,8 @@
"karma-firefox-launcher": "^2.1.3", "karma-firefox-launcher": "^2.1.3",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~2.1.0",
"prettier": "^3.2.5", "prettier": "^3.3.2",
"typescript": "~5.4.5" "typescript": "~5.4.5",
"typescript-eslint": "^8.0.0-alpha.30"
} }
} }

@ -2,7 +2,6 @@ import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(() => beforeEach(() =>
@ -10,7 +9,6 @@ describe('AppComponent', () => {
imports: [ imports: [
RouterModule.forRoot([]), RouterModule.forRoot([]),
AppComponent, AppComponent,
HttpClientModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
], ],
}) })

@ -4,22 +4,17 @@ import { LandingPageComponent } from './components/landing-page/landing-page.com
import { DocumentationComponent } from './components/documentation/documentation.component'; import { DocumentationComponent } from './components/documentation/documentation.component';
import { FormComponent } from './components/form/form.component'; import { FormComponent } from './components/form/form.component';
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
import { OurStoryComponent } from './components/our-story/our-story.component';
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.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 // Toutes les routes de l'application sont définies ici
export const routes: Routes = [ export const routes: Routes = [
{ path: '', component: LandingPageComponent }, { path: '', component: LandingPageComponent },
{ path: 'work/:work', component: EditorComponent },
{ path: 'works', component: WorksListComponent },
{ path: 'editor', component: EditorComponent }, { path: 'editor', component: EditorComponent },
{ path: 'editor-live/:idRoom', component: EditorComponent }, { path: 'editor-live/:idRoom', component: EditorComponent },
{ path: 'documentation', component: DocumentationComponent }, { path: 'documentation', component: DocumentationComponent },
{ path: 'contact', component: FormComponent }, { path: 'contact', component: FormComponent },
{ path: 'our-story', component: OurStoryComponent },
{ path: 'terms-of-service', component: TermsOfServiceComponent }, { path: 'terms-of-service', component: TermsOfServiceComponent },
{ path: 'privacy-policy', component: PrivacyPolicyComponent }, { path: 'privacy-policy', component: PrivacyPolicyComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'login', component: LoginComponent },
]; ];

@ -51,27 +51,6 @@
</svg> </svg>
</button> </button>
</div> </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>
<div class="editor-section-bar-header"> <div class="editor-section-bar-header">
@if (errorMessage) { @if (errorMessage) {
@ -93,10 +72,9 @@
<div class="param-editor"> <div class="param-editor">
<button <button
class="button-icon button-run" class="button-icon button-run"
type="button"
(click)="onRunButtonClicked()" (click)="onRunButtonClicked()"
[disabled]="isLoaded"> [disabled]="isLoaded">
<div style="margin-right: 10px">{{ 'Editor.Run' | translate }}</div> <div>RUN</div>
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

@ -1,14 +1,3 @@
@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 { #editor-bar-header {
display: flex; display: flex;
align-items: center; align-items: center;
@ -93,18 +82,3 @@ select {
::ng-deep .codemirror6-editor { ::ng-deep .codemirror6-editor {
height: 100%; 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,8 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditorComponent } from './editor.component'; import { EditorComponent } from './editor.component';
import { HttpClientModule } from '@angular/common/http';
import { TranslateModule } from '@ngx-translate/core';
describe('EditorComponent', () => { describe('EditorComponent', () => {
let component: EditorComponent; let component: EditorComponent;
@ -10,7 +8,7 @@ describe('EditorComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditorComponent, HttpClientModule, TranslateModule.forRoot()], imports: [EditorComponent],
}); });
fixture = TestBed.createComponent(EditorComponent); fixture = TestBed.createComponent(EditorComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

@ -5,7 +5,6 @@ import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
import { LanguageDescription } from '@codemirror/language'; import { LanguageDescription } from '@codemirror/language';
import { CODE_DEFAULTS, LANGUAGES } from '../languages'; import { CODE_DEFAULTS, LANGUAGES } from '../languages';
import { SafeHTMLPipe } from '../../safe-html.pipe'; import { SafeHTMLPipe } from '../../safe-html.pipe';
import { TranslateModule } from '@ngx-translate/core';
import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { import {
@ -38,11 +37,12 @@ import {
closeBracketsKeymap, closeBracketsKeymap,
} from '@codemirror/autocomplete'; } from '@codemirror/autocomplete';
import { lintKeymap } from '@codemirror/lint'; import { lintKeymap } from '@codemirror/lint';
import { Work } from '../../models/work.model'; import {
import { WorkService } from '../../services/work.service'; Connection,
import { Connection, getDocument, peerExtension } from '../../services/connection.service'; getDocument,
peerExtension,
} from '../../services/connection.service';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { LocationStrategy } from '@angular/common';
const basicSetup: Extension = (() => [ const basicSetup: Extension = (() => [
highlightActiveLineGutter(), highlightActiveLineGutter(),
@ -82,7 +82,6 @@ const basicSetup: Extension = (() => [
ReactiveFormsModule, ReactiveFormsModule,
FormsModule, FormsModule,
SafeHTMLPipe, SafeHTMLPipe,
TranslateModule,
], ],
}) })
export class EditorComponent { export class EditorComponent {
@ -152,8 +151,8 @@ export class EditorComponent {
this.client = new WebSocket(`${environment.webSocketUrl}/live/${idRoom}`); this.client = new WebSocket(`${environment.webSocketUrl}/live/${idRoom}`);
this.client.addEventListener('open', async () => { this.client.addEventListener('open', async () => {
let conn = new Connection(this.client!); const conn = new Connection(this.client!);
let { version, doc } = await getDocument(conn); const { version, doc } = await getDocument(conn);
this.codemirror.editor?.dispatch({ this.codemirror.editor?.dispatch({
changes: { changes: {
@ -168,37 +167,9 @@ export class EditorComponent {
}); });
} }
#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( constructor(
private router: Router, private router: Router,
private backendService: BackendService, private backendService: BackendService
private workService: WorkService,
private locationStrategy: LocationStrategy,
) { ) {
backendService.getResult().subscribe((msg) => { backendService.getResult().subscribe((msg) => {
if (msg.type === 'stdout' || msg.type === 'stderr') { if (msg.type === 'stdout' || msg.type === 'stderr') {
@ -252,54 +223,4 @@ export class EditorComponent {
a.href = URL.createObjectURL(blob); a.href = URL.createObjectURL(blob);
a.click(); 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);
});
}
}
} }

@ -5,27 +5,11 @@
</head> </head>
<div class="footer"> <div class="footer">
<span class="footer--rights">{{ 'FooterPage.Rights' | translate }}</span> <span class="footer--rights"
<span class="footer__links"> >Copyright © 2024 Sandcasten. All rights reserved.</span
<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 class="footer__links">
<a>Mentions légales</a> - <a>Politique de confidentialité</a> -
<a>Conditions générales d'utilisation</a>
</span> </span>
</div> </div>

@ -19,8 +19,6 @@
a { a {
cursor: pointer; cursor: pointer;
text-decoration: none;
color: $color-gray;
} }
} }
} }

@ -1,8 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component'; import { FooterComponent } from './footer.component';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
describe('FooterComponent', () => { describe('FooterComponent', () => {
let component: FooterComponent; let component: FooterComponent;
@ -10,7 +8,7 @@ describe('FooterComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [FooterComponent, RouterModule.forRoot([]), TranslateModule.forRoot()], imports: [FooterComponent],
}); });
fixture = TestBed.createComponent(FooterComponent); fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

@ -27,13 +27,6 @@
</div> </div>
<nav class="right_part--menu"> <nav class="right_part--menu">
<a
routerLink="/works"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }"
>{{ 'HeaderPage.Work' | translate }}</a
>
<a <a
routerLink="editor" routerLink="editor"
routerLinkActive="active" routerLinkActive="active"
@ -81,11 +74,7 @@
<div class="right_part__connexion"> <div class="right_part__connexion">
<!--Login--> <!--Login-->
<div <div class="right_part__connexion--login">
class="right_part__connexion--login"
routerLink="/login"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
<span>Log In</span> <span>Log In</span>
<svg <svg
width="30" width="30"
@ -106,17 +95,9 @@
</svg> </svg>
</div> </div>
<!--Register--> <!--Register-->
<button <div class="right_part__connexion--register">
class="right_part__connexion--register"
routerLink="/register"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
<span>Register Now</span> <span>Register Now</span>
</button> </div>
<!-- Logout -->
<button class="right_part__connexion--logout" (click)="logout()">
<span>Logout</span>
</button>
</div> </div>
</div> </div>
</div> </div>
@ -166,11 +147,7 @@
</nav> </nav>
<div class="mobile_menu__connexion"> <div class="mobile_menu__connexion">
<!--Login--> <!--Login-->
<div <div class="right_part__connexion--login">
class="right_part__connexion--login"
routerLink="/login"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
<span>Log In</span> <span>Log In</span>
<svg <svg
width="30" width="30"
@ -191,17 +168,9 @@
</svg> </svg>
</div> </div>
<!--Register--> <!--Register-->
<button <div class="right_part__connexion--register">
class="right_part__connexion--register"
routerLink="/register"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
<span>Register Now</span> <span>Register Now</span>
</button> </div>
<!-- Logout -->
<button class="right_part__connexion--logout" (click)="logout()">
<span>Logout</span>
</button>
</div> </div>
</div> </div>
</div> </div>

@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component'; import { HeaderComponent } from './header.component';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
describe('HeaderComponent', () => { describe('HeaderComponent', () => {
let component: HeaderComponent; let component: HeaderComponent;
@ -14,7 +13,6 @@ describe('HeaderComponent', () => {
imports: [ imports: [
RouterModule.forRoot([]), RouterModule.forRoot([]),
HeaderComponent, HeaderComponent,
HttpClientModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
], ],
}); });

@ -1,11 +1,15 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import {
Component,
ElementRef,
Input,
ViewChild,
} from '@angular/core';
import { TranslationService } from '../../services/translation.service'; import { TranslationService } from '../../services/translation.service';
import { ThemeService } from '../../services/theme.service'; import { ThemeService } from '../../services/theme.service';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { Router, RouterLink, RouterLinkActive } from '@angular/router'; import { RouterLink, RouterLinkActive } from '@angular/router';
import { NgClass, NgOptimizedImage } from '@angular/common'; import { NgClass, NgOptimizedImage } from '@angular/common';
import { UserService } from 'src/app/services/user.service';
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
@ -26,18 +30,12 @@ export class HeaderComponent {
version: string = '1.0'; version: string = '1.0';
isMenuOpen: boolean = false; isMenuOpen: boolean = false;
isCheck: boolean = false; isCheck: boolean = false;
linkLastWork = ''; //peut-être à cast en string
@ViewChild('menuRef') menuRef!: ElementRef; @ViewChild('menuRef') menuRef!: ElementRef;
@Input() themeClass!: string; @Input() themeClass!: string;
@Input() themeService!: ThemeService; @Input() themeService!: ThemeService;
// Instanciation du service pour les actions de traduction // Instanciation du service pour les actions de traduction
constructor( constructor(private translationService: TranslationService) {}
private router: Router,
private translationService: TranslationService,
private userService: UserService
) {}
// Méthode pour changer la langue // Méthode pour changer la langue
onLanguageChange(event: Event) { onLanguageChange(event: Event) {
@ -64,20 +62,4 @@ export class HeaderComponent {
document.body.classList.remove('no-scroll'); 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');
}
});
}
} }

@ -17,7 +17,8 @@
class="landing-page__hero_left__buttons--editor--image" class="landing-page__hero_left__buttons--editor--image"
src="assets/img/logo.png" src="assets/img/logo.png"
alt="editor icon" /> alt="editor icon" />
<span>{{ 'LandingPage.Bouton1' | translate }}</span> <!--TODO - Add trad-->
<span>Editeur de code</span>
</button> </button>
<a href="https://codefirst.iut.uca.fr/git/sandkasten"> <a href="https://codefirst.iut.uca.fr/git/sandkasten">
<button class="landing-page__hero_left__buttons--git"> <button class="landing-page__hero_left__buttons--git">
@ -25,7 +26,8 @@
class="landing-page__hero_left__buttons--git--image" class="landing-page__hero_left__buttons--git--image"
src="assets/img/landing-page/gitea.png" src="assets/img/landing-page/gitea.png"
alt="editor icon" /> alt="editor icon" />
<span>{{ 'LandingPage.Bouton2' | translate }}</span> <!--TODO - Add trad-->
<span>Dépôt Git</span>
</button> </button>
</a> </a>
</div> </div>
@ -33,8 +35,8 @@
<div class="landing-page__hero_right"> <div class="landing-page__hero_right">
<img <img
class="landing-page__hero_right--image" class="landing-page__hero_right--image"
src="assets/img/logo.png" src="assets/img/landing-page/cross.png"
alt="Sandkasten Logo" /> alt="landing page" />
</div> </div>
</div> </div>
<!--endregion--> <!--endregion-->
@ -43,11 +45,12 @@
<div class="landing-page__informations"> <div class="landing-page__informations">
<div class="landing-page__informations_top"> <div class="landing-page__informations_top">
<h2 class="landing-page__informations_top--title"> <h2 class="landing-page__informations_top--title">
{{ 'LandingPage.Top.Tittle' | translate }} Ce dont vous avez besoin de savoir sur notre éditeur
</h2> </h2>
<span class="landing-page__informations_top--description">{{ <span class="landing-page__informations_top--description"
'LandingPage.Top.Description' | translate >Astro comes batteries included. It takes the best parts of
}}</span> state-of-the-art tools and adds its own innovations.</span
>
</div> </div>
<div class="landing-page__informations--lists"> <div class="landing-page__informations--lists">
<!--SECURITE--> <!--SECURITE-->
@ -70,11 +73,12 @@
</g> </g>
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">Sécurisé</div>
{{ 'LandingPage.List.Tittle1' | translate }}
</div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text1' | translate }} Pour garantir la solidité de notre outil nous exécutons votre code
sur un environnement fermé et limitant les requêtes trop coûteuse.
Ce procédé nous protège ainsi des tentatives de mauvaise utilisation
du système.
</div> </div>
</div> </div>
<!--Import--> <!--Import-->
@ -94,10 +98,12 @@
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">
{{ 'LandingPage.List.Tittle2' | translate }} Import / Export
</div> </div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text2' | translate }} Vous pouvez importez des fichiers et l'éditeur va reconnaître le
langage à partir de son extension. Vous pouvez aussi exporter le
code que vous avez fait en seulement un clic.
</div> </div>
</div> </div>
<!--Options--> <!--Options-->
@ -133,11 +139,11 @@
</g> </g>
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">Options</div>
{{ 'LandingPage.List.Tittle3' | translate }}
</div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text3' | translate }} Vous disposez de nombreuses options vous permettant de personnalisé
et d'optimiser votre utilisation de Sandkasten. Ces options sont
directement disponible depuis l'éditeur.
</div> </div>
</div> </div>
<!--Multi-language--> <!--Multi-language-->
@ -166,10 +172,13 @@
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">
{{ 'LandingPage.List.Tittle4' | translate }} Multi-language
</div> </div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text4' | translate }} De nombreux langages de codes sont reconnus dans Sandkasten. Lorsque
vous taper du code ou en importer un fichier l'éditeur reconnaît
automatiquement le langage. Vous pouvez aussi directement le
sélectionner dans l'éditeur.
</div> </div>
</div> </div>
<!--TODO--> <!--TODO-->
@ -188,11 +197,11 @@
</g> </g>
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">Lorem ipsum</div>
{{ 'LandingPage.List.Tittle5' | translate }}
</div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text5' | translate }} Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam,
</div> </div>
</div> </div>
<!--TODO--> <!--TODO-->
@ -211,11 +220,11 @@
</g> </g>
</svg> </svg>
</div> </div>
<div class="landing-page__informations__list--title"> <div class="landing-page__informations__list--title">Lorem ipsum</div>
{{ 'LandingPage.List.Tittle6' | translate }}
</div>
<div class="landing-page__informations__list--description"> <div class="landing-page__informations__list--description">
{{ 'LandingPage.List.Text6' | translate }} Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam,
</div> </div>
</div> </div>
</div> </div>
@ -224,9 +233,9 @@
<!--region Technologies--> <!--region Technologies-->
<div class="landing-page__technologies"> <div class="landing-page__technologies">
<span class="landing-page__technologies--title">{{ <span class="landing-page__technologies--title"
'LandingPage.Technologie.Tittle' | translate >Languages disponibles dès maintenant</span
}}</span> >
<div class="landing-page__technologies__list"> <div class="landing-page__technologies__list">
<!--C--> <!--C-->
<a href="https://www.open-std.org/jtc1/sc22/wg14/"> <a href="https://www.open-std.org/jtc1/sc22/wg14/">
@ -304,10 +313,12 @@
<div class="landing-page__about"> <div class="landing-page__about">
<div class="landing-page__about__titles"> <div class="landing-page__about__titles">
<h2 class="landing-page__about--title"> <h2 class="landing-page__about--title">
{{ 'LandingPage.About.Tittle' | translate }} Empowering the world with Astro.
</h2> </h2>
<div class="landing-page__about--description"> <div class="landing-page__about--description">
{{ 'LandingPage.About.Text' | translate }} We're a multi-cultural team from around the world! We come from
diverse backgrounds, bringing different personalities, experiences and
skills to the job. This is what makes our team so special.
</div> </div>
</div> </div>
<div class="landing-page__about__members"> <div class="landing-page__about__members">
@ -319,7 +330,7 @@
<div class="landing-page__about__member--texts"> <div class="landing-page__about__member--texts">
<div class="landing-page__about__member--texts--name">Bastien</div> <div class="landing-page__about__member--texts--name">Bastien</div>
<div class="landing-page__about__member--texts--role"> <div class="landing-page__about__member--texts--role">
{{ 'LandingPage.MemberRole' | translate }} Développeur
</div> </div>
</div> </div>
</div> </div>
@ -331,7 +342,7 @@
<div class="landing-page__about__member--texts"> <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--name">Clément</div>
<div class="landing-page__about__member--texts--role"> <div class="landing-page__about__member--texts--role">
{{ 'LandingPage.MemberRole' | translate }} Développeur
</div> </div>
</div> </div>
</div> </div>
@ -343,7 +354,7 @@
<div class="landing-page__about__member--texts"> <div class="landing-page__about__member--texts">
<div class="landing-page__about__member--texts--name">Colin</div> <div class="landing-page__about__member--texts--name">Colin</div>
<div class="landing-page__about__member--texts--role"> <div class="landing-page__about__member--texts--role">
{{ 'LandingPage.MemberRole' | translate }} Développeur
</div> </div>
</div> </div>
</div> </div>
@ -355,7 +366,7 @@
<div class="landing-page__about__member--texts"> <div class="landing-page__about__member--texts">
<div class="landing-page__about__member--texts--name">Hugo</div> <div class="landing-page__about__member--texts--name">Hugo</div>
<div class="landing-page__about__member--texts--role"> <div class="landing-page__about__member--texts--role">
{{ 'LandingPage.MemberRole' | translate }} Développeur
</div> </div>
</div> </div>
</div> </div>
@ -367,7 +378,7 @@
<div class="landing-page__about__member--texts"> <div class="landing-page__about__member--texts">
<div class="landing-page__about__member--texts--name">Matis</div> <div class="landing-page__about__member--texts--name">Matis</div>
<div class="landing-page__about__member--texts--role"> <div class="landing-page__about__member--texts--role">
{{ 'LandingPage.MemberRole' | translate }} Développeur
</div> </div>
</div> </div>
</div> </div>
@ -377,9 +388,9 @@
<!--region Socials--> <!--region Socials-->
<div class="landing-page__socials"> <div class="landing-page__socials">
<span class="landing-page__socials--title">{{ <span class="landing-page__socials--title"
'LandingPage.SocialTittle' | translate >Rejoinez-nous sur nos réseaux</span
}}</span> >
<div class="landing-page__socials__list"> <div class="landing-page__socials__list">
<!--Twitter--> <!--Twitter-->
<a href="https://twitter.com/Sandkasten3a"> <a href="https://twitter.com/Sandkasten3a">
@ -443,14 +454,13 @@
<!--region Get Started--> <!--region Get Started-->
<div class="landing-page__get_started"> <div class="landing-page__get_started">
<h3 class="landing-page__get_started--title"> <h3 class="landing-page__get_started--title">Allez essayer Sandkasten</h3>
{{ 'LandingPage.GetStarted.Tittle' | translate }}
</h3>
<div class="landing-page__get_started--description"> <div class="landing-page__get_started--description">
{{ 'LandingPage.GetStarted.Description' | translate }} Rejoignez dès maintenant la communauté Sandkasten en testant notre
éditeur !
</div> </div>
<button class="landing-page__get_started--button" routerLink="/editor"> <button class="landing-page__get_started--button" routerLink="/editor">
{{ 'LandingPage.GetStarted.Button' | translate }} Get Started
</button> </button>
</div> </div>
<!--endregion--> <!--endregion-->

@ -21,9 +21,7 @@ export class LandingPageComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.themeService.isDarkTheme.subscribe((value) => { this.themeService.isDarkTheme.subscribe((value) => {
value this.themeClass = value ? 'dark-theme' : 'light-theme';
? (this.themeClass = 'dark-theme')
: (this.themeClass = 'light-theme');
}); });
} }
// Si click sur "Run", on redirige vers la page d'édition // Si click sur "Run", on redirige vers la page d'édition

@ -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 = '';
}
});
}
}

@ -0,0 +1,36 @@
<body>
<main>
<div>
<img class="colin" [src]="colin" alt="Photo-Colin" />
<h1>COLIN FRIZOT</h1>
<p>Bonjour, je suis Colin, un poisson développeur. (fraude)</p>
<img class="hugo" [src]="hugo" alt="Photo-Hugo" />
<h1>HUGO PRADIER</h1>
<p>
Bonjour, je suis Hugo, un développeur bientôt chauve. (suit tous les
tutos en ligne, mais ça marche jamais)
</p>
<img class="bastien" [src]="bastien" alt="Photo-Bastien" />
<h1>BASTIEN OLLIER</h1>
<p>
Bonjour, je suis Bastien, un développeur visuellement parlant, très
puant. (connaît l'adresse de la grand-mère de Colin)
</p>
<img class="clement" [src]="clement" alt="Photo-Clement" />
<h1>CLÉMENT FRÉVILLE</h1>
<p>
Bonjour, je suis Clément, un développeur meilleur que tes profs. (dieu)
</p>
<img class="matis" [src]="matis" alt="Photo-Matis" />
<h1>MATIS MAZINGUE</h1>
<p>
Bonjour, je suis Matis, un "développeur". (est là que pour l'argent,
mais il est nul)
</p>
</div>
</main>
</body>

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

@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-our-story',
templateUrl: './our-story.component.html',
styleUrl: './our-story.component.scss',
standalone: true,
})
export class OurStoryComponent {
constructor(private router: Router) {}
colin: string = 'assets/img/colin.png';
hugo: string = 'assets/img/hugo.png';
bastien: string = 'assets/img/bastien.png';
clement: string = 'assets/img/clement.png';
matis: string = 'assets/img/matis.png';
// Si click sur "Run", on redirige vers la page de notre histoire
onContinue(): void {
this.router.navigateByUrl('/our-story');
}
}

@ -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,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);
}
}

@ -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;
}

@ -3,10 +3,10 @@ import { SSE } from 'sse.js';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
export type ExecutionMessage = { export interface ExecutionMessage {
type: 'stdout' | 'stderr' | 'exit'; type: 'stdout' | 'stderr' | 'exit';
text: string; text: string;
}; }
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -16,8 +16,6 @@ export class BackendService {
private resultSubject = new Subject<ExecutionMessage>(); private resultSubject = new Subject<ExecutionMessage>();
constructor() {}
async createRoom(code: string) { async createRoom(code: string) {
const reponse = await fetch(`${this.apiUrl}/live`, { const reponse = await fetch(`${this.apiUrl}/live`, {
method: 'POST', method: 'POST',

@ -47,7 +47,7 @@ function pushUpdates(
fullUpdates: readonly Update[] fullUpdates: readonly Update[]
): Promise<boolean> { ): Promise<boolean> {
// Strip off transaction data // Strip off transaction data
let updates = fullUpdates.map((u) => ({ const updates = fullUpdates.map((u) => ({
clientID: u.clientID, clientID: u.clientID,
changes: u.changes.toJSON(), changes: u.changes.toJSON(),
})); }));
@ -76,7 +76,7 @@ export function getDocument(
} }
export function peerExtension(startVersion: number, connection: Connection) { export function peerExtension(startVersion: number, connection: Connection) {
let plugin = ViewPlugin.fromClass( const plugin = ViewPlugin.fromClass(
class { class {
private pushing = false; private pushing = false;
private done = false; private done = false;
@ -90,11 +90,11 @@ export function peerExtension(startVersion: number, connection: Connection) {
} }
async push() { async push() {
let updates = sendableUpdates(this.view.state); const updates = sendableUpdates(this.view.state);
if (this.pushing || !updates.length) return; if (this.pushing || !updates.length) return;
this.pushing = true; this.pushing = true;
let version = getSyncedVersion(this.view.state); const version = getSyncedVersion(this.view.state);
await pushUpdates(connection, version, updates); await pushUpdates(connection, version, updates);
this.pushing = false; this.pushing = false;
// Regardless of whether the push failed or new updates came in // Regardless of whether the push failed or new updates came in
@ -105,8 +105,8 @@ export function peerExtension(startVersion: number, connection: Connection) {
async pull() { async pull() {
while (!this.done) { while (!this.done) {
let version = getSyncedVersion(this.view.state); const version = getSyncedVersion(this.view.state);
let updates = await pullUpdates(connection, version); const updates = await pullUpdates(connection, version);
this.view.dispatch(receiveUpdates(this.view.state, updates)); this.view.dispatch(receiveUpdates(this.view.state, updates));
} }
} }

@ -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 });
}
}

@ -2,53 +2,21 @@
"LandingPage": { "LandingPage": {
"Welcome": "Welcome to Sandkasten", "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!", "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 !", "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": { "HeaderPage": {
"Work": "Work",
"Editor": "Editor", "Editor": "Editor",
"Documentation": "Documentation", "Documentation": "Documentation",
"Contact": "Contact us" "Contact": "Contact us"
}, },
"FooterPage": { "FooterPage": {
"About": "About Sandkasten",
"Contact": "Contact us",
"Story": "Our story",
"Legal": "Legal mentions", "Legal": "Legal mentions",
"Terms": "Terms of use", "Terms": "Terms of use",
"Privacy": "Privacy policy", "Privacy": "Privacy policy",
"Rights": "Copyright © 2024 Sandkasten. All rights reserved." "Rights": "©2024 Sandkasten, Inc. All rights reserved."
}, },
"TermsOfServicePage": { "TermsOfServicePage": {
"Mentions": "LEGALS MENTIONS", "Mentions": "LEGALS MENTIONS",
@ -147,11 +115,5 @@
"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." "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" "Date": "This document was last updated on January 10, 2024"
},
"Editor": {
"Share": "Share",
"Save": "Save",
"Run": "RUN",
"Add": "ADD BDD"
} }
} }

@ -2,53 +2,21 @@
"LandingPage": { "LandingPage": {
"Welcome": "Bienvenue sur Sandkasten", "Welcome": "Bienvenue sur Sandkasten",
"Description": "Vous voici arrivé sur le meilleur site bac à sable de test de code ! Nous vous permettons de tester tous vos programmes sur vos langages préférés. Grâce à léditeur Code Mirror et à notre gestion personalisée de lexécution sur des conteneurs, votre code devient complétement inoffensif et vous pouvez donc vous amusez autant que vous le souhaitez !", "Description": "Vous voici arrivé sur le meilleur site bac à sable de test de code ! Nous vous permettons de tester tous vos programmes sur vos langages préférés. Grâce à léditeur Code Mirror et à notre gestion personalisée de lexécution sur des conteneurs, votre code devient complétement inoffensif et vous pouvez donc vous amusez autant que vous le souhaitez !",
"Try": "Essayez maintenant !", "Try": "Essayez maintenant !"
"Bouton1": "Editeur de code",
"Bouton2": "Dépôt Git",
"Top": {
"Tittle": "Ce dont vous avez besoin de savoir sur notre éditeur",
"Description": "Sandkasten, l'éditeur qui possède toutes les options dont vous avez besoin. Avec ces avantages vous pourrez tester votre code et en voir le résultat."
},
"List": {
"Tittle1": "Sécurité",
"Text1": "Pour garantir la solidité de notre outil nous exécutons votre code sur un environnement fermé et limitant les requêtes trop coûteuse. Ce procédé nous protège ainsi des tentatives de mauvaise utilisation du système.",
"Tittle2": "Importer / Exporter",
"Text2": "Vous pouvez importez des fichiers et l'éditeur va reconnaître le langage à partir de son extension. Vous pouvez aussi exporter le code que vous avez fait en seulement un clic.",
"Tittle3": "Options",
"Text3": "Vous disposez de nombreuses options vous permettant de personnalisé et d'optimiser votre utilisation de Sandkasten. Ces options sont directement disponible depuis l'éditeur.",
"Tittle4": "Multi-languages",
"Text4": "De nombreux langages de codes sont reconnus dans Sandkasten. Lorsque vous taper du code ou en importer un fichier l'éditeur reconnaît automatiquement le langage. Vous pouvez aussi directement le sélectionner dans l'éditeur.",
"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": "Renforcer le monde avec Sandkasten.",
"Text": "Nous sommes une équipe se professionalisant dans le monde des technologies ! Nous apportons des personnalités, des expériences et des compétences différentes. C'est ce qui rend notre équipe si spéciale."
},
"MemberRole": "Développeur",
"SocialTittle": "Rejoinez-nous sur nos réseaux",
"GetStarted": {
"Tittle": "Allez essayer Sandkasten",
"Description": "Rejoignez dès maintenant la communauté Sandkasten en testant notre éditeur !",
"Button": "Commencez !"
}
}, },
"HeaderPage": { "HeaderPage": {
"Work": "Travail",
"Editor": "Éditeur", "Editor": "Éditeur",
"Documentation": "Documentation", "Documentation": "Documentation",
"Contact": "Nous contacter" "Contact": "Nous contacter"
}, },
"FooterPage": { "FooterPage": {
"About": "À propos de Sandkasten",
"Contact": "Nous contacter",
"Story": "Notre histoire",
"Legal": "Mentions légales", "Legal": "Mentions légales",
"Terms": "Conditions d'utilisation", "Terms": "Conditions d'utilisation",
"Privacy": "Politique de confidentialité", "Privacy": "Politique de confidentialité",
"Rights": "Copyright © 2024 Sandkasten. Tous droits réservés" "Rights": "©2024 Sandkasten, Inc. Tous droits réservés"
}, },
"TermsOfServicePage": { "TermsOfServicePage": {
"Mentions": "MENTIONS LÉGALES", "Mentions": "MENTIONS LÉGALES",
@ -147,11 +115,5 @@
"Text2": "Nous nous efforcerons de résoudre les plaintes et les litiges et ferons tous les efforts raisonnables pour répondre à votre souhait d'exercer vos droits le plus rapidement possible et, en tout état de cause, dans les délais prévus par les lois applicables en matière de protection des données." "Text2": "Nous nous efforcerons de résoudre les plaintes et les litiges et ferons tous les efforts raisonnables pour répondre à votre souhait d'exercer vos droits le plus rapidement possible et, en tout état de cause, dans les délais prévus par les lois applicables en matière de protection des données."
}, },
"Date": "Ce document a été mis à jour pour la dernière fois le 10 janvier 2024." "Date": "Ce document a été mis à jour pour la dernière fois le 10 janvier 2024."
},
"Editor": {
"Share": "Partager",
"Save": "Sauvegarder",
"Run": "EXECUTER",
"Add": "AJOUTER A LA BDD"
} }
} }

@ -11,14 +11,8 @@
<link <link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
rel="stylesheet" /> rel="stylesheet" />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
rel="stylesheet" />
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet" />
</head> </head>
<body class="mat-typography"> <body>
<app-root></app-root> <app-root></app-root>
</body> </body>
</html> </html>

@ -2,15 +2,10 @@ import { createTranslateLoader } from './app/app.module';
import { importProvidersFrom } from '@angular/core'; import { importProvidersFrom } from '@angular/core';
import { AppComponent } from './app/app.component'; import { AppComponent } from './app/app.component';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { import { withInterceptorsFromDi, provideHttpClient, HttpClient } from '@angular/common/http';
withInterceptorsFromDi,
provideHttpClient,
HttpClient,
} from '@angular/common/http';
import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser';
import { TranslationService } from './app/services/translation.service'; import { TranslationService } from './app/services/translation.service';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter, withComponentInputBinding } from '@angular/router'; import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app/app.routes'; import { routes } from './app/app.routes';
@ -32,6 +27,5 @@ bootstrapApplication(AppComponent, {
), ),
TranslationService, TranslationService,
provideHttpClient(withInterceptorsFromDi()), provideHttpClient(withInterceptorsFromDi()),
provideAnimationsAsync(),
], ],
}).catch(console.error); }).catch(console.error);

@ -6,20 +6,19 @@
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"esModuleInterop": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
"useDefineForClassFields": false, "useDefineForClassFields": false,
"allowSyntheticDefaultImports": true,
"lib": ["ES2022", "dom"], "lib": ["ES2022", "dom"],
}, },
"angularCompilerOptions": { "angularCompilerOptions": {

Loading…
Cancel
Save