diff --git a/.gitignore b/.gitignore index 27fa5eb..cd1b3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ node_modules/ package-lock\.json + +src/assets/images/contents/ + +dist/ diff --git a/angular.json b/angular.json index 52df64f..be23e47 100644 --- a/angular.json +++ b/angular.json @@ -24,8 +24,9 @@ "tsConfig": "tsconfig.app.json", "aot": true, "assets": [ - "src/favicon.ico", - "src/assets" + "src/assets/images/favicon.png", + "src/assets", + "src/apis" ], "styles": [ "src/assets/scss/main.scss" diff --git a/auth/identity.pem b/auth/identity.pem new file mode 100644 index 0000000..0fff9d4 --- /dev/null +++ b/auth/identity.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEApNcN8cn0872DhGSdPobrIR9kBfd+qSL/Sqrgk3mywEuuzhN3 +MOYbaejwdIJacC0OjCMVgm3f2TziefYd2ssvAUjT+M9FCESUSSDHgPVxt22UDZxz +ZraasA+jdVW0QQtBv8AjyDCXAmMZxcGT1x1htKsKfG2JDxxc1DXub1BaBSkrqcZ1 +1u50UK0DBbRcUhd85z9Oivju3IktDaVow09fpwUR/tK1xO8PPsaAUQMKdN1ArGA/ +O+FFXAirQ7OD8tT4gCmJYrU2h4mAlcjKwpvmXbtHGmZ2tn4nJsaYUxL0vMPHWoTl +ymOkFbKujBDkTSdKgUdTBy7Bda9ZzAcEBJoGgQIDAQABAoIBAEGVmuO3obEUlu4n +BfUpwwVzst044nkzBnXT1PR4OCmQMyWk0whulTunRXxlnMwC8UXKc7VoN+b79XPm ++2vg6XvOWSAmf2XRu1n5I8doYG1FuOFNfRDB2HvyTOvTRJuYeflr3hC5XGvDuC5Z +XZP6CbTTVKG7BwwvEbQRHSPGyXpBiqd+OOfTpeoobrkGJJAKxvXHO0G7zgB5h/H+ +0fjSWvo4QxzOAF74f0qUmWBbvLjWL3yZhVE8mxNcVoV1HlIdXV5anMe9FURci5D4 +U8ehGky3sbHjcc7wro5IPWJYTrDAmvhI8wMMx7gbHe2HOzyWRX++ifjfP77kw2e3 +IYnwMAECgYEA05Ehmsipk4rmJeCgmDJomQVsJgMW1zjOJwIljSZ9o55+LHpma6r0 +O6sdQYgiNwQT0f//iJZkatpDAyC3U6EU9RY7Yoc0R3XK86vryKQlB/Nh1boLldt6 +9TGdL5eCzaraiYuLjGvXMGTS8LjB0ftFCGQswl2kcuOFx1YjE4WPoUECgYEAx3Wo +yenPXHCdD4kIrQ9SxPha2aobh33MjdwSYSiDliaEoBg/voWJExFmMJuvhR46xqmy +i8E0RuzzBrG4Zhbdo4kcpdF2GtAuxNw8uYvG+SQub8zAmBT7YpXzTmpGQ2TR3i8S +XzD57s7C1mSkBIwWuVJYcx3Kgm1mQNIOELU31UECgYEAhNc14HhqcaffRp06eRX9 +s0dCVsPNzalvV/LzHSOz886KrubT9HrNC8Ivhnwx75Vx1IQHMP4tYyJUvVwHgE0+ +WX1yIDWAz/XYTxP94meekNVy8r30lE3RcK+MYNujV/wVaBPktXDpFwvXnyqDGJPL +Dq/HouslXLYbw8QEFjfgrYECgYB+oSU6ozThpCEihsY6ULskj+PlwohdubEO8wO8 +KSN5RRT4Ksz1YQPIVkiBXaXOJoX8MCpJbayJxs73lgbS0Xt+4oKMh3GqzjaTBpuK +1MHK1Hyiv+QZ6WA7k6V3SCM5kB1pKItKYeabBStPP2+d725R04SR+PzjVx8O0gzZ +8KL0wQKBgC7LRztq8PMDGTHGXVxWXwZ08ZAf2MAsBvYyAU5xudS+pRqlNGDgyxiv +YQEe9n2DVu7GVsoNnKIapmXV1qx0vTU/CpKV0cpXi6m3XVnbtjBsaYtkYNNZfHVl +E++gP4qgvALKugnzaGn5oby0PPcFFhnNqRwFb5c3diihwMQhkudG +-----END RSA PRIVATE KEY----- diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..0d0a48f --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +yarn prod +rsync -avz --delete --exclude '/apis/conn.conn' --exclude '.well-known' --exclude '/uploads' -e "ssh -i ./auth/identity.pem -p2222" ./dist/dslak-website/* cdr@2.238.194.8:/www/dslak.it/ + diff --git a/package.json b/package.json index 5c909c1..a68d2bd 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "start": "ng serve", "dev": "ng serve", "build": "ng build", + "prod": "ng build --prod --configuration production", + "deploy": "sh ./deploy.sh", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" @@ -20,6 +22,11 @@ "@angular/platform-browser": "~9.1.7", "@angular/platform-browser-dynamic": "~9.1.7", "@angular/router": "~9.1.7", + "@kolkov/angular-editor": "^1.1.4", + "bootstrap": "^4.5.3", + "hammerjs": "^2.0.8", + "ng-particles": "^2.1.11", + "ngx-image-gallery": "^2.0.5", "rxjs": "~6.5.4", "tslib": "^1.10.0", "zone.js": "~0.10.2" diff --git a/src/apis/auth.php b/src/apis/auth.php new file mode 100755 index 0000000..1cd0185 --- /dev/null +++ b/src/apis/auth.php @@ -0,0 +1,41 @@ +status = 200; + +$data = json_decode(file_get_contents("php://input")); + +if(isset($_GET['act']) && $_GET['act'] == 'login') { + if(isset($data->usr) && $data->usr == 'admin' && isset($data->pwd) && $data->pwd == 'JohnHolmes') { + http_response_code(200); + $content->status = 200; + $content->authToken = base64_encode('admin:JohnHolmes'.date("Y-m-d")); + } else { + http_response_code(401); + $content->status = 401; + $content->action = 'login'; + } +} else if(isset($_GET['act']) && $_GET['act'] == 'check') { + if(isset($data->token) && $data->token == base64_encode('admin:JohnHolmes'.date("Y-m-d"))) { + http_response_code(200); + $content->status = 200; + $content->authToken = base64_encode('admin:JohnHolmes'.date("Y-m-d")); + } else { + http_response_code(200); + $content->status = 401; + $content->action = 'check'; + $content->token = $data->token; + } +} + +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: POST"); +header("Access-Control-Max-Age: 3600"); + +echo json_encode($content); + +?> diff --git a/src/apis/conn.conn b/src/apis/conn.conn new file mode 100755 index 0000000..877d806 --- /dev/null +++ b/src/apis/conn.conn @@ -0,0 +1,16 @@ + diff --git a/src/apis/exhibition.php b/src/apis/exhibition.php new file mode 100644 index 0000000..e008311 --- /dev/null +++ b/src/apis/exhibition.php @@ -0,0 +1,80 @@ +token) && $data->token == base64_encode('admin:JohnHolmes'.date("Y-m-d"))) { + + if(isset($_GET['act'])) { + if($_GET['act'] == 'save') { + if(isset($data->id)) { + $q = mysqli_query($conn,"UPDATE `exhibitions` SET title = '".addslashes($data->title)."', content = '".addslashes($data->content)."', + tags = '".$data->tags."', date_from = '".$data->date_from."', date_to = '".$data->date_to."', + image = '".$data->image."', works = '".$data->works."', gallery = '".$data->gallery."', + videos = '".$data->videos."' WHERE id = ".$data->id.""); + } else { + $q = mysqli_query($conn,"INSERT INTO `exhibitions` + (`id`, `title`, `content`, `tags`, `date_from`, `date_to`, `image`, `works`, `gallery`, `videos`) + VALUES (NULL, '".addslashes($data->title)."', '".addslashes($data->content)."', '".$data->tags."', + '".$data->date_from."', '".$data->date_to."', '".$data->image."', '".$data->works."', + '".$data->gallery."', '".$data->videos."')"); + } + + if($q) { + http_response_code(201); + $content->status = 201; + } else { + http_response_code(403); + $content->status = "UPDATE `exhibitions` SET title = '".addslashes($data->title)."', content = '".addslashes($data->content)."', + tags = '".$data->tags."', date_from = '".$data->date_from."', date_to = '".$data->date_to."', + image = '".$data->image."', works = '".$data->works."', gallery = '".$data->gallery."', + videos = '".$data->videos."' WHERE id = ".$data->id.""; + } + } + + if($_GET['act'] == 'delete') { + if(isset($data->id)) { + $q = mysqli_query($conn,"DELETE FROM `exhibitions` WHERE id = ".$data->id.""); + if($q) { + http_response_code(201); + $content->status = 201; + } else { + http_response_code(403); + $content->status = 403; + } + } + } + + if($q) { + $qe = mysqli_query($conn,"SELECT * FROM `exhibitions` ORDER BY id DESC"); + if(mysqli_num_rows($qe) > 0) { + $content->items = array(); + while($re = mysqli_fetch_array($qe)) { + $item = null; + $item->id = $re['id']; + $item->title = $re['title']; + $item->date_from = $re['date_from']; + $item->date_to = $re['date_to']; + $item->tags = $re['tags']; + $item->image = $re['image']; + array_push($content->items, $item); + } + } + } + } + +} else { + http_response_code(401); + $content->status = 401; +} +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: POST"); +header("Access-Control-Max-Age: 3600"); + +echo json_encode($content); + +?> diff --git a/src/apis/index.php b/src/apis/index.php new file mode 100644 index 0000000..b91173e --- /dev/null +++ b/src/apis/index.php @@ -0,0 +1,99 @@ +items = array(); + switch($_GET['query']) { + case "portfolio": + case "installations": + case "entertainment": + case "performances": + case "workshops": + if($_GET['query'] == 'portfolio') {$filter = '';} else {$filter = "WHERE type='".$_GET['query']."'";} + if($_GET['random']) {$order = 'ORDER BY RAND()';} else {$order = "ORDER BY id DESC";} + $qe = mysqli_query($conn,"SELECT * FROM `works` $filter $order"); + if(mysqli_num_rows($qe) > 0) { + $content = null; + $content->items = array(); + while($re = mysqli_fetch_array($qe)) { + $item = null; + $item->id = $re['id']; + $item->title = $re['title']; + $item->type = $re['type']; + $item->tags = $re['tags']; + $item->image = $re['image']; + array_push($content->items, $item); + } + } + break; + case "exhibitions": + $qe = mysqli_query($conn,"SELECT * FROM `exhibitions` ORDER BY date_from DESC"); + if(mysqli_num_rows($qe) > 0) { + $content = null; + $content->items = array(); + while($re = mysqli_fetch_array($qe)) { + $item = null; + $item->id = $re['id']; + $item->title = $re['title']; + $item->date_from = $re['date_from']; + $item->date_to = $re['date_to']; + $item->tags = $re['tags']; + $item->image = $re['image']; + array_push($content->items, $item); + } + } + break; + case "detail": + $qe = mysqli_query($conn,"SELECT * FROM `".$_GET['type']."` WHERE id=".$_GET['id']); + if(mysqli_num_rows($qe)>0) { + $content = null; + $re = mysqli_fetch_array($qe); + $item = null; + $item->id = $re['id']; + $item->title = $re['title']; + $item->content = $re['content']; + $item->tags = $re['tags']; + $item->image = $re['image']; + $item->videos = $re['videos']; + $item->gallery = $re['gallery']; + if($_GET['type'] == 'exhibitions') { + $item->date_from = $re['date_from']; + $item->date_to = $re['date_to']; + $item->works = array(); + $qx = mysqli_query($conn,"SELECT id,title FROM `works` WHERE id IN (".$re['works'].")"); + while($re = mysqli_fetch_array($qx)) { + $ex = null; + $ex->id = $re['id']; + $ex->title = $re['title']; + array_push($item->works, $ex); + } + } else if($_GET['type'] == 'works') { + $item->type = $re['type']; + $item->exhibitions = array(); + $qx = mysqli_query($conn,"SELECT id,title FROM `exhibitions` WHERE id IN (".$re['exhibitions'].")"); + while($re = mysqli_fetch_array($qx)) { + $ex = null; + $ex->id = $re['id']; + $ex->title = $re['title']; + array_push($item->exhibitions, $ex); + } + } + $content->item = $item; + } + break; + } +} +http_response_code(200); + +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: GET"); +header("Access-Control-Max-Age: 3600"); +echo json_encode($content); + +?> diff --git a/src/apis/remove.php b/src/apis/remove.php new file mode 100644 index 0000000..8f3b4a0 --- /dev/null +++ b/src/apis/remove.php @@ -0,0 +1,24 @@ +token) && $data->token == base64_encode('admin:JohnHolmes'.date("Y-m-d"))) { + + @unlink('..'.$data->url); + http_response_code(200); + $content->status = 200; + +} else { + http_response_code(401); + $content->status = 401; +} + +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: POST"); +header("Access-Control-Max-Age: 3600"); + +echo json_encode($content); + +?> diff --git a/src/apis/upload.php b/src/apis/upload.php new file mode 100644 index 0000000..39e84a7 --- /dev/null +++ b/src/apis/upload.php @@ -0,0 +1,35 @@ +status = 200; + $content->imageUrl = $path."/".$filename; + + } else { + http_response_code(401); + $content->status = 401; + $content->megssage = 'No file uploaded'; + } +} else { + http_response_code(401); + $content->status = 401; +} + +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: POST"); +header("Access-Control-Max-Age: 3600"); + +echo json_encode($content); + +?> diff --git a/src/apis/work.php b/src/apis/work.php new file mode 100644 index 0000000..9ec5edf --- /dev/null +++ b/src/apis/work.php @@ -0,0 +1,75 @@ +token) && $data->token == base64_encode('admin:JohnHolmes'.date("Y-m-d"))) { + + if(isset($_GET['act'])) { + if($_GET['act'] == 'save') { + if(isset($data->id)) { + $q = mysqli_query($conn,"UPDATE `works` SET title = '".addslashes($data->title)."', content = '".addslashes($data->content)."', + type = '".$data->type."', tags = '".$data->tags."', image = '".$data->image."', + exhibitions = '".$data->exhibitions."', gallery = '".$data->gallery."', videos = '".$data->videos."' + WHERE id = ".$data->id.""); + } else { + $q = mysqli_query($conn,"INSERT INTO `works` (`id`, `title`, `content`, `type`, `tags`, `image`, `exhibitions`, `gallery`, `videos`) + VALUES (NULL, '".addslashes($data->title)."', '".addslashes($data->content)."', '".$data->type."', + '".$data->tags."', '".$data->image."', '".$data->exhibitions."', '".$data->gallery."', + '".$data->videos."')"); + } + + if($q) { + http_response_code(201); + $content->status = 201; + } else { + http_response_code(403); + $content->status = 403; + } + } + + if($_GET['act'] == 'delete') { + if(isset($data->id)) { + $q = mysqli_query($conn,"DELETE FROM `works` WHERE id = ".$data->id.""); + if($q) { + http_response_code(201); + $content->status = 201; + } else { + http_response_code(403); + $content->status = 403; + } + } + } + + if($q) { + $qe = mysqli_query($conn,"SELECT * FROM `works` ORDER BY id DESC"); + if(mysqli_num_rows($qe) > 0) { + $content->items = array(); + while($re = mysqli_fetch_array($qe)) { + $item = null; + $item->id = $re['id']; + $item->title = $re['title']; + $item->type = $re['type']; + $item->tags = $re['tags']; + $item->image = $re['image']; + array_push($content->items, $item); + } + } + } + } + +} else { + http_response_code(401); + $content->status = 401; +} +header("Access-Control-Allow-Origin: *"); +header("Content-Type: application/json; charset=UTF-8"); +header("Access-Control-Allow-Methods: POST"); +header("Access-Control-Max-Age: 3600"); + +echo json_encode($content); + +?> diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html new file mode 100644 index 0000000..980843a --- /dev/null +++ b/src/app/about/about.component.html @@ -0,0 +1,42 @@ +
+
+
+ + +

DSLAK è un progetto che nasce nel 2010 da un'idea di Carmine De Rosa, sviluppatore ed amministratore di sistema in ambiente Unix/Linux, dopo aver completato gli studi in ambito elettronico approfondisce le proprie conoscenze informatiche presso l'Università di Salerno dove insieme ad un gruppo di appassionati di haking fonda HCSSLUG (Linux Users Group dell'Università di Salerno) e ed il relativo HackLab con il quale realizza numerosi progetti in ambito OpenSource, basati sulla ricerca e sulla sperimentazione in ambito tecnologico.

+ +

Nel 2006 si avvicina alle arti digitali e nel 2009, incuriosito dapprima dall'aspetto tecnico ma senza tralasciare quello scenico e concettuale, inizia ad approfondire le propie conoscenze nell'ambito della new-media art e delle installazioni interattive, studiando le soluzioni adottate da artisti affermati e sviluppando varie soluzioni alternative che utilizzano però un approccio più sostenibile, efficiente e soprattutto open.

+ +

Nel 2011 realizza le sue prime installazioni interattive e ad oggi è alla continua ricerca di soluzioni creative.

+ +

DSLAK ha ad oggi all'attivo numerosi progetti, come workshops sull'utilizzo di sensori e microcontrollori nel campo dell'interattività e delle arti digitali, installazioni interattive e performances live frutto della sperimentazione e della ricerca continua, oltre alle collaborazioni con diversi artisti nazionali ed internazionali.

+ + + +
+
diff --git a/src/app/about/about.component.scss b/src/app/about/about.component.scss new file mode 100644 index 0000000..22ceada --- /dev/null +++ b/src/app/about/about.component.scss @@ -0,0 +1,86 @@ +@import "../../assets/scss/variables"; + +.component-about { + .content { + position: relative; + margin: 150px auto 80px auto; + padding: 40px 50px; + font-size: $font-18; + text-align: justify; + background: $white-alpha; + color: $black; + box-shadow: 0px 0px 25px $white-alpha; + border-radius: 10px; + z-index: 1; + + + .about-links { + color: $black; + + .link { + display: flex; + text-decoration: none; + margin: 0; + padding: 0; + line-height: 35px; + width: 200px; + transition: transform .3s; + -webkit-backface-visibility: hidden; + + .icon { + display: inline-block; + font-size: 15px; + padding: 5px; + margin: 5px; + background: $dark-gray; + border-radius: 2px; + color: $white; + height: 25px; + width: 25px; + text-align: center; + } + + .label { + display: inline-block; + color: $dark-gray; + font-size: $font-16; + padding-left: 5px; + + } + + &:hover { + transform: scale(1.1); + } + } + } + + .back { + position: absolute; + top: -40px; + left: 0px; + height: 40px; + width: 60px; + appearance: none; + border: none; + padding: 0; + font-size: $font-40; + color: $black-alpha; + background: transparent; + cursor: pointer; + transition: transform .3s; + -webkit-backface-visibility: hidden; + + &:hover { + transform: scale(1.1) translateX(-10px); + } + } + } +} + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .component-about { + .content { + transform: rotate(2deg) skew(0deg, -6deg); + } + } +} diff --git a/src/app/about/about.component.spec.ts b/src/app/about/about.component.spec.ts new file mode 100644 index 0000000..6b77344 --- /dev/null +++ b/src/app/about/about.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AboutComponent } from './about.component'; + +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AboutComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AboutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts new file mode 100644 index 0000000..204648a --- /dev/null +++ b/src/app/about/about.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit } from '@angular/core' +import { Location } from '@angular/common' + +@Component({ + selector: 'app-about', + templateUrl: './about.component.html', + styleUrls: ['./about.component.scss'] +}) +export class AboutComponent implements OnInit { + + constructor( + private location: Location + ) { } + + ngOnInit(): void { + } + + back() { + this.location.back() + } +} diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html new file mode 100644 index 0000000..4bcd7dc --- /dev/null +++ b/src/app/admin/admin.component.html @@ -0,0 +1,191 @@ +
+
+
+ +
+
+ +
+
+ +
+
+
+ {{sectionTitle}} +
+
+ +
+
+
+
+ +
+
+
+
+ Title + +
+
+ Type + +
+
+ Date from + +
+
+ Date to + +
+
+ Content + +
+
+ Tags + +
+
+ Gallery + +
+ +
+ Exhibitions + + + Selected exhibitions + + {{se.date_from | date}} | {{se.title}} + +
+ +
+ Works + + + Selected works + + {{sw.type}} | {{sw.title}} + + +
+ +
+ Video +
+ +
+
+ +
+
+ +
+ + Selected Videos + + {{sv.type}} | {{sv.url}} + +
+
+ +
+
+ + +
+
+ Title +
{{type}} | {{title}}
+
{{dateFrom}} | {{title}}
+
+
+ Content +
+
+ +
+ Gallery + +
+ +
+ +
+
+
+
+
+
+ diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss new file mode 100644 index 0000000..be80feb --- /dev/null +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,210 @@ +@import "../../assets/scss/variables"; + +.component-admin { + + .login-form-container { + text-align: center; + padding: 30px 40px; + color: $white; + + .login-label { + font-size: $font-14; + color: $black; + padding: 8px; + } + + .button { + width: 300px; + } + } + + .edit-container { + .title { + display: block; + font-size: $font-30; + font-weight: bolder; + text-transform: uppercase; + padding: 20px 0; + } + .form { + .label { + display: block; + font-size: $font-20; + text-transform: uppercase; + padding: 20px 0 5px 0; + } + + .gallery-container { + display: flex; + background: $white; + border-radius: 4px; + width: 100%; + padding: 5px; + min-height: 100px; + + .image-add { + appearance: none; + display: inline-block; + position: relative; + border: 2px solid $light-gray; + border-radius: 4px; + height: 100px; + width: 100px; + margin: 5px; + cursor: pointer; + + &:before { + content: '\e90a'; + font-family: $font-icon; + font-size: $font-30; + color: $light-gray; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + input { + visibility: hidden; + } + } + + .image-box { + display: inline-block; + position: relative; + border: 2px solid $light-gray; + height: 100px; + width: 120px; + margin: 5px; + border-radius: 4px; + overflow: hidden; + + .image { + position: absolute; + height: 100%; + width: 100%; + object-fit: cover; + z-index: 0; + } + + .remove, + .set-main { + position: absolute; + top: 4px; + right: 4px; + border: 0; + border-radius: 2px; + color: $black; + height: 20px; + width: 20px; + background: $white-alpha; + padding: 0; + margin: 0; + cursor: pointer; + font-size: $font-12; + z-index: 10; + } + + .remove { + color: $red; + } + .set-main { + color: $green; + right: 30px; + } + + &.main { + border: 2px solid $yellow; + } + } + } + + .selected-exhibition, + .selected-work, + .selected-video { + display: block; + position: relative; + font-size: $font-16; + font-weight: bolder; + border-radius: 4px; + border: 2px solid $white; + background: $white-alpha; + cursor: pointer; + padding: 8px 50px 8px 15px; + margin-bottom: 5px; + + &:before { + content: '\e903'; + position: absolute; + top: 8px; + right: 10px; + font-family: $font-icon; + font-size: $font-20; + color: $gray; + } + } + + .preview-box { + border-radius: 4px; + background: $white-alpha2; + padding: 10px; + width: 100%; + } + } + } + + .menu { + background: $dark-gray; + + .section-title { + display: block; + width: 100%; + padding: 50px 10px 10px; + font-size: $font-22; + font-weight: bolder; + text-transform: uppercase; + color: $white; + text-align: center; + border-bottom: 1px solid $black-alpha; + } + + .action { + display: block; + appearance: none; + border: none; + border-radius: 0px; + width: 100%; + padding: 10px; + font-size: $font-14; + text-transform: uppercase; + color: $white; + background: $dark-gray; + cursor: pointer; + border-bottom: 1px solid $black-alpha; + + &.active { + background: $yellow; + color: $black; + border: none; + } + } + } +} + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .component-admin { + .menu { + position: fixed; + height: 100vh; + width: 25%; + } + } +} + +@media (min-width: map-get($grid-breakpoints, 'lg')) { + .component-admin { + .menu { + width: 16.66%; + } + } +} diff --git a/src/app/admin/admin.component.spec.ts b/src/app/admin/admin.component.spec.ts new file mode 100644 index 0000000..72e742f --- /dev/null +++ b/src/app/admin/admin.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminComponent } from './admin.component'; + +describe('AdminComponent', () => { + let component: AdminComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdminComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdminComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts new file mode 100644 index 0000000..03443d9 --- /dev/null +++ b/src/app/admin/admin.component.ts @@ -0,0 +1,437 @@ +import { Component, OnInit } from '@angular/core' +import { ApisService } from '../services/apis.service' +import { AuthService } from '../services/auth.service' +import { AngularEditorConfig } from '@kolkov/angular-editor' +import { environment } from '../../environments/environment' +import { editorConfig } from '../../config/config' + +@Component({ + selector: 'app-admin', + templateUrl: './admin.component.html', + styleUrls: ['./admin.component.scss'] +}) +export class AdminComponent implements OnInit { + + private restApi = `${environment.API_URL}` + public basePath = `${environment.BASE_PATH}` + + public authCheck: boolean = false + public userName: string = '' + public password: string = '' + public activeEditor: string = '' + public sectionTitle: string = '' + public activeModify: boolean = false + public modifyId: number = null + + public exhibitions: any = [] + public works: any = [] + public selectedExhibitions: any = [] + public selectedWorks: any = [] + public selectedVideos: any = [] + public selectedGallery: any = [] + + // ngModels + public title: string = '' + public type: string = '' + public content: string = '' + public tags: string = '' + public dateFrom: string = '' + public dateTo: string = '' + public mainImage: string = '' + public videoType: string = '' + public videoURL: string = '' + + public editorConfig: AngularEditorConfig = editorConfig + public workSections: any = [ + {title: 'Entertainment', section: 'entertainment'}, + {title: 'Installations', section: 'installations'}, + {title: 'Performances', section: 'performances'}, + {title: 'Workshops', section: 'workshops'} + ] + + constructor( + private authService: AuthService, + private apisService: ApisService + ) { } + + ngOnInit(): void { + + const body = { token: window.sessionStorage.getItem('authToken') } + this.authService.authCheck(body).toPromise().then((response) => { + this.authCheck = response.status && response.status == 200 + + if(this.authCheck) { + this.loadData() + } + + },(error) => { + this.authCheck = false + console.error('Auth ERROR INIT', error) + }).catch((e) => { + this.authCheck = false + console.error('Auth CATCH INIT', e) + }) + } + + login(): void { + const body = { usr: this.userName, pwd: this.password } + this.authService.login(body).toPromise().then((response) => { + this.authCheck = response.status && response.status == 200 + if(this.authCheck) { + window.sessionStorage.setItem('authToken', response.authToken) + this.loadData() + } + },(error) => { + console.error('Auth ERROR', error) + }).catch((e) => { + console.error('Auth CATCH', e) + }) + } + + loadData(): void { + this.apisService.getPortfolio('exhibitions').toPromise().then((response) => { + this.exhibitions = response.items + },(error) => { + console.error('getPortfolio ERROR', error) + }).catch((e) => { + console.error('getPortfolio CATCH', e) + }) + + this.apisService.getPortfolio('portfolio').toPromise().then((response) => { + this.works = response.items + },(error) => { + console.error('getPortfolio ERROR', error) + }).catch((e) => { + console.error('getPortfolio CATCH', e) + }) + } + + showEditor(section): void { + switch(section) { + case 'works-add': + this.sectionTitle = 'Add work' + break; + case 'works-modify': + this.sectionTitle = 'Modify work' + break; + case 'works-delete': + this.sectionTitle = 'Delete work' + break; + case 'exhibitions-add': + this.sectionTitle = 'Add exhibition' + break; + case 'exhibitions-modify': + this.sectionTitle = 'Modify exhibition' + break; + case 'exhibitions-delete': + this.sectionTitle = 'Delete exhibition' + break; + } + this.activeModify = false + this.activeEditor = section + this.modifyId = null + this.resetFields() + } + + exhibitionAdd(id): void { + this.selectedExhibitions.push( + this.exhibitions.filter(item => item.id == id)[0] + ) + this.exhibitions = this.exhibitions.filter(item => item.id != id) + } + + exhibitionRemove(id): void { + this.exhibitions.push( + this.selectedExhibitions.filter(item => item.id == id)[0] + ) + this.selectedExhibitions = this.selectedExhibitions.filter(item => item.id != id) + } + + workAdd(id): void { + this.selectedWorks.push( + this.works.filter(item => item.id == id)[0] + ) + this.works = this.works.filter(item => item.id != id) + } + + workRemove(id): void { + this.works.push( + this.selectedWorks.filter(item => item.id == id)[0] + ) + this.selectedWorks = this.selectedWorks.filter(item => item.id != id) + } + + videoAdd(): void { + this.selectedVideos.push({ + type: this.videoType, + url: this.videoURL.replace(/\"/g, "\\\"") + }) + this.videoURL = '' + } + + videoRemove(url): void { + this.selectedVideos = this.selectedVideos.filter(item => item.url != url) + } + + onFileChanged(e) { + const file = (e.target).files[0] + const uploadData = new FormData() + uploadData.append('token', window.sessionStorage.getItem('authToken')) + uploadData.append('path', 'assets') + uploadData.append('file', file, file.name) + this.apisService.uploadImage(uploadData).toPromise().then((response) => { + this.selectedGallery.push({ + title: response.title || '', + url: response.imageUrl + }) + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + gallerySetMain(url): void { + this.selectedGallery.forEach((e) => { + if(e.url == url) { + e.main = true + } else { + delete e.main + } + }) + } + + galleryRemove(url): void { + const body = { + token: window.sessionStorage.getItem('authToken'), + url: url + } + this.apisService.removeImage(body).toPromise().then((response) => { + this.selectedGallery = this.selectedGallery.filter(item => item.url != url) + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + selectWork(id): void { + this.activeModify = true + this.modifyId = id + this.apisService.getDetails('works', id).toPromise().then((response) => { + const detail = response.item + this.apisService.getPortfolio('exhibitions').toPromise().then((response) => { + this.exhibitions = response.items + this.title = detail.title + this.content = detail.content + this.type = detail.type + this.tags = detail.tags + this.selectedExhibitions = detail.exhibitions.length ? + this.exhibitions.filter(item => detail.exhibitions.map(a => a.id).indexOf(item.id) > -1) : [] + this.selectedGallery = detail.gallery ? JSON.parse(detail.gallery) : [] + this.selectedVideos = detail.videos ? JSON.parse(detail.videos) : [] + + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + selectExhibition(id): void { + + this.activeModify = true + this.modifyId = id + this.apisService.getDetails('exhibitions', id).toPromise().then((response) => { + const detail = response.item + this.apisService.getPortfolio('portfolio').toPromise().then((response) => { + this.works = response.items + this.title = detail.title + this.content = detail.content + this.tags = detail.tags + this.dateFrom = detail.date_from + this.dateTo = detail.date_to + this.selectedWorks = detail.works.length ? + this.works.filter(item => detail.works.map(a => a.id).indexOf(item.id) > -1) : [] + this.selectedGallery = detail.gallery ? JSON.parse(detail.gallery) : [] + this.selectedVideos = detail.videos ? JSON.parse(detail.videos) : [] + + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + saveData(): void { + if(this.activeEditor == 'works-add' || this.activeEditor == 'works-modify') { + this.saveWork() + } + if(this.activeEditor == 'exhibitions-add' || this.activeEditor == 'exhibitions-modify') { + this.saveExhibition() + } + } + + saveWork(): void { + let error = false + let errorMessages = [] + const mainImage = this.selectedGallery.filter(item => item.main) + + if(!this.title){ + error = true + errorMessages.push('No title') + } + if(!this.type){ + error = true + errorMessages.push('No type selected') + } + + if(this.selectedGallery.length == 0 || mainImage.length == 0){ + error = true + errorMessages.push('No main image selected') + } + + if(error) { + console.log('ERRORS:',errorMessages) + } else { + const body = { + id: this.activeModify ? this.modifyId : null, + token: window.sessionStorage.getItem('authToken'), + title: this.title, + content: this.content, + type: this.type, + tags: this.tags, + image: mainImage[0].url, + exhibitions: this.selectedExhibitions.map(a => a.id).join(','), + gallery: JSON.stringify(this.selectedGallery), + videos: JSON.stringify(this.selectedVideos) + } + + this.apisService.saveWork(body).toPromise().then((response) => { + this.resetFields() + this.works = response.items + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + } + + saveExhibition(): void { + let error = false + let errorMessages = [] + const mainImage = this.selectedGallery.filter(item => item.main) + + if(!this.title){ + error = true + errorMessages.push('No title') + } + if(!this.dateFrom){ + error = true + errorMessages.push('No date from selected') + } + if(!this.dateTo){ + error = true + errorMessages.push('No date to selected') + } + if(this.selectedGallery.length == 0 || mainImage.length == 0){ + error = true + errorMessages.push('No main image selected') + } + + if(error) { + console.log('ERRORS:',errorMessages) + } else { + const body = { + id: this.activeModify ? this.modifyId : null, + token: window.sessionStorage.getItem('authToken'), + title: this.title, + content: this.content, + date_from: this.dateFrom, + date_to: this.dateTo, + tags: this.tags, + image: mainImage[0].url, + works: this.selectedWorks.map(a => a.id).join(','), + gallery: JSON.stringify(this.selectedGallery), + videos: JSON.stringify(this.selectedVideos) + } + + this.apisService.saveExhibition(body).toPromise().then((response) => { + this.resetFields() + this.exhibitions = response.items + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + } + + deleteData(id): void { + if(this.activeEditor == 'works-delete') { + this.deleteWork(id) + } + if(this.activeEditor == 'exhibitions-delete') { + this.deleteExhibition(id) + } + } + + deleteWork(id): void { + + const body = { + id: id, + token: window.sessionStorage.getItem('authToken') + } + + this.apisService.deleteWork(body).toPromise().then((response) => { + this.resetFields() + this.works = response.items + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + deleteExhibition(id): void { + + const body = { + id: id, + token: window.sessionStorage.getItem('authToken') + } + + this.apisService.deleteExhibition(body).toPromise().then((response) => { + this.resetFields() + this.exhibitions = response.items + },(error) => { + console.error(error) + }).catch((e) => { + console.error(e) + }) + } + + resetFields(): void { + this.title = '' + this.content = '' + this.type = '' + this.tags = '' + this.dateFrom = '' + this.dateTo = '' + this.selectedExhibitions = [] + this.selectedWorks = [] + this.selectedGallery = [] + this.selectedVideos = [] + this.modifyId = null + } +} diff --git a/src/app/app-layout/app-layout.component.html b/src/app/app-layout/app-layout.component.html index 04d51c8..e46bcfe 100644 --- a/src/app/app-layout/app-layout.component.html +++ b/src/app/app-layout/app-layout.component.html @@ -1,2 +1,7 @@ - -

app-layout works!

+
+ + + + +
diff --git a/src/app/app-layout/app-layout.component.scss b/src/app/app-layout/app-layout.component.scss index e69de29..0d2866d 100644 --- a/src/app/app-layout/app-layout.component.scss +++ b/src/app/app-layout/app-layout.component.scss @@ -0,0 +1,3 @@ +main { + overflow: hidden; +} diff --git a/src/app/app-layout/app-layout.component.ts b/src/app/app-layout/app-layout.component.ts index ec8cf2b..26318ab 100644 --- a/src/app/app-layout/app-layout.component.ts +++ b/src/app/app-layout/app-layout.component.ts @@ -1,4 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core' +import { Router } from '@angular/router' +import type { Container } from 'tsparticles' @Component({ selector: 'app-app-layout', @@ -7,9 +9,98 @@ import { Component, OnInit } from '@angular/core'; }) export class AppLayoutComponent implements OnInit { - constructor() { } + public page: string = '/' + public particlesEnabled: boolean = false + public id: string = 'tsparticles' + + public particlesOptions: any = { + background: { + color: { + value: "transparent" + } + }, + fpsLimit: 60, + interactivity: { + detectsOn: "canvas", + events: { + onClick: { + enable: true, + mode: "push" + }, + onHover: { + enable: true, + mode: "repulse" + }, + resize: true + }, + modes: { + bubble: { + distance: 200, + duration: 2, + opacity: 0.8, + size: 40 + }, + push: { + quantity: 6 + }, + repulse: { + distance: 100, + duration: 0.4 + } + } + }, + particles: { + color: { + value: "#fff" + }, + links: { + color: "#fff", + distance: 150, + enable: true, + opacity: 0.7, + width: 1 + }, + collisions: { + enable: true + }, + move: { + direction: "none", + enable: true, + outMode: "bounce", + random: false, + speed: 2, + straight: false + }, + number: { + density: { + enable: true, + value_area: 600 + }, + value: 90 + }, + opacity: { + value: 0.5 + }, + shape: { + type: "circle" + }, + size: { + random: true, + value: 5 + } + }, + detectRetina: true + } + + constructor(private router: Router) { } ngOnInit(): void { + this.page = this.router.url + this.particlesEnabled = this.page != '/admin' } + + particlesLoaded(container: Container): void { + //console.log('particlesLoaded', container) + } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 9f96b1f..d21da2f 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,13 +1,37 @@ import { NgModule } from '@angular/core' import { Routes, RouterModule } from '@angular/router' import { AppLayoutComponent } from './app-layout/app-layout.component' - +import { AboutComponent } from './about/about.component' +import { PortfolioComponent } from './portfolio/portfolio.component' +import { DetailComponent } from './detail/detail.component' +import { HomeComponent } from './home/home.component' +import { AdminComponent } from './admin/admin.component' const routes: Routes = [ { path: '', component: AppLayoutComponent, - children: [] + children: [ + //{ path: '', redirectTo: '/portfolio', pathMatch: 'full' }, + { path: '', component: HomeComponent }, + { path: 'about', component: AboutComponent }, + { path: 'portfolio', component: PortfolioComponent }, + { path: 'exhibitions', component: PortfolioComponent }, + { path: 'installations', component: PortfolioComponent }, + { path: 'entertainment', component: PortfolioComponent }, + { path: 'performances', component: PortfolioComponent }, + { path: 'workshops', component: PortfolioComponent }, + { path: 'detail', component: DetailComponent, + children: [ + { path: '**', component: DetailComponent, + children: [ + { path: '**', component: DetailComponent } + ] + } + ] + }, + { path: 'admin', component: AdminComponent } + ] } ] diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a10ec68..cf7935c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,20 +1,42 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { HttpClientModule } from '@angular/common/http'; +import { NgParticlesModule } from "ng-particles"; +import { NgxImageGalleryModule } from 'ngx-image-gallery'; +import { FormsModule } from '@angular/forms'; +import { AngularEditorModule } from '@kolkov/angular-editor'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { HeaderComponent } from './header/header.component'; import { AppLayoutComponent } from './app-layout/app-layout.component'; +import { AboutComponent } from './about/about.component'; +import { PortfolioComponent } from './portfolio/portfolio.component'; +import { DetailComponent } from './detail/detail.component'; +import { AdminComponent } from './admin/admin.component'; +import { HomeComponent } from './home/home.component'; +import { SpinnerComponent } from './spinner/spinner.component'; @NgModule({ declarations: [ AppComponent, HeaderComponent, - AppLayoutComponent + AppLayoutComponent, + AboutComponent, + PortfolioComponent, + DetailComponent, + AdminComponent, + HomeComponent, + SpinnerComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + NgParticlesModule, + NgxImageGalleryModule, + FormsModule, + AngularEditorModule, + HttpClientModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/detail/detail.component.html b/src/app/detail/detail.component.html new file mode 100644 index 0000000..488b0f3 --- /dev/null +++ b/src/app/detail/detail.component.html @@ -0,0 +1,76 @@ +
+
+
+ +

{{details.title}}

+ +
+ from + on + {{details.date_from | date}} + to {{details.date_to | date}} +
+ + + +
+ +
+
+ {{video.title}} +
+ +
+
+ +
+
+ +
+
+ + + Tags: {{details.tags}} + + Exhibitions: + {{exhibition.title}} + + + Works: + {{work.title}} + +
+
+ + + + diff --git a/src/app/detail/detail.component.scss b/src/app/detail/detail.component.scss new file mode 100644 index 0000000..86015ae --- /dev/null +++ b/src/app/detail/detail.component.scss @@ -0,0 +1,152 @@ +@import "../../assets/scss/variables"; + +.component-detail { + + .content { + position: relative; + margin: 150px auto 80px auto; + padding: 40px 50px; + background: $white-alpha; + color: $black; + box-shadow: 0px 0px 25px $white-alpha; + border-radius: 10px; + z-index: 1; + + .title { + margin: 0; + font-size: $font-34; + font-weight: bold; + text-transform: uppercase; + } + + .text { + font-size: $font-18; + text-align: justify; + padding: 40px 0; + } + + .date-container { + display: inline-flex; + + .date { + display: inline-flex; + margin: auto; + font-size: $font-20; + } + + .date-indication { + margin: 2px 5px auto 5px; + font-size: $font-12; + } + } + + .gallery { + padding-top: 40px; + + .gallery-title { + font-size: $font-18; + font-weight: bold; + padding-bottom: 5px; + } + + .gallery-container { + position: relative; + //padding-bottom: 56.25%; + height: 180px; + + .image{ + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + cursor: pointer; + transition: transform .4s; + + &:hover { + transform: scale(1.2) rotate(1deg); + z-index: 10; + } + } + } + } + + .videos { + padding-bottom: 40px; + + .video-title { + font-size: $font-18; + font-weight: bold; + padding-bottom: 5px; + } + + .youtube, + .vimeo, + .embed { + position: relative; + padding-bottom: 56.25%; + + iframe, + .iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + } + } + + .tags, + .links { + display: block; + font-size: $font-12; + text-transform: uppercase; + padding: 5px 0; + + .link { + display: inline-block; + font-size: $font-12; + text-transform: uppercase; + padding: 0 5px; + cursor: pointer; + } + } + + .back { + position: absolute; + top: -40px; + left: 0px; + height: 40px; + width: 60px; + appearance: none; + border: none; + padding: 0; + font-size: $font-40; + color: $black-alpha; + background: transparent; + cursor: pointer; + transition: transform .3s; + -webkit-backface-visibility: hidden; + + &:hover { + transform: scale(1.1) translateX(-10px); + } + } + } +} + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .component-detail { + .content { + transform: rotate(2deg) skew(0deg, -6deg); + + .date-container { + position: absolute; + top: 40px; + right: 40px; + } + } + } +} diff --git a/src/app/detail/detail.component.spec.ts b/src/app/detail/detail.component.spec.ts new file mode 100644 index 0000000..149b9be --- /dev/null +++ b/src/app/detail/detail.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DetailComponent } from './detail.component'; + +describe('DetailComponent', () => { + let component: DetailComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/detail/detail.component.ts b/src/app/detail/detail.component.ts new file mode 100644 index 0000000..00d9403 --- /dev/null +++ b/src/app/detail/detail.component.ts @@ -0,0 +1,184 @@ +import { Component, OnInit, ViewChild, ViewEncapsulation} from '@angular/core' +import { Router, NavigationEnd, NavigationStart, ActivatedRoute } from '@angular/router' +import { DomSanitizer } from '@angular/platform-browser' +import { Location } from '@angular/common' +import { NgxImageGalleryComponent, GALLERY_IMAGE, GALLERY_CONF } from "ngx-image-gallery" +import { ApisService } from '../services/apis.service' +import { environment } from '../../environments/environment' + +@Component({ + selector: 'app-detail', + templateUrl: './detail.component.html', + styleUrls: ['./detail.component.scss'], + encapsulation: ViewEncapsulation.None, +}) +export class DetailComponent implements OnInit { + + public basePath = `${environment.BASE_PATH}` + public loaded = false + public init = false + + @ViewChild(NgxImageGalleryComponent) ngxImageGallery: NgxImageGalleryComponent + + public details: any = {} + public section: string = '' + public id: number = 0 + private history: string[] = [] + + public conf: GALLERY_CONF = { + imageOffset: '0px', + showDeleteControl: false, + showImageTitle: false, + } + + public galleryImages: GALLERY_IMAGE[] = [] + public loadedImages: boolean[] = [] + + constructor( + private apisService: ApisService, + private router: Router, + private location: Location, + private activeRoute: ActivatedRoute, + private sanitizer: DomSanitizer + ) { } + + ngOnInit(): void { + this.section = this.router.url.split('/')[2] + this.id = parseInt(this.router.url.split('/')[3]) + this.showDetails(this.section, this.id) + } + + ngAfterContentInit() { + this.init = true + } + + showDetails(section, id, title = ''): void { + this.galleryImages = [] + this.apisService.getDetails(section, id).toPromise().then((response) => { + if(this.history[this.history.length - 1] != `/detail/${section}/${id}`) { + this.history.push(`/detail/${section}/${id}/${title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '_')}`) + } + + const detail = response.item + detail.videos = detail.videos ? JSON.parse(detail.videos) : [] + detail.videos.forEach((e) => { + e.code = e.url.split('/').pop() + switch(e.type) { + case 'youtube': + e.embed = this.sanitizer.bypassSecurityTrustResourceUrl(`https://www.youtube.com/embed/${e.code}`) + break + case 'vimeo': + e.embed = this.sanitizer.bypassSecurityTrustResourceUrl(`https://player.vimeo.com/video/${e.code}`) + break + case 'embed': + e.embed = this.sanitizer.bypassSecurityTrustHtml(e.url) + break + } + }) + + detail.gallery = detail.gallery ? JSON.parse(detail.gallery) : [] + + detail.gallery.forEach((e) => { + if(!e.main) { + this.galleryImages.push({ + url: `${this.basePath}${e.url}`, + altText: e.title, + title: e.title, + thumbnailUrl: `${this.basePath}${e.url}` + }) + this.loadedImages.push(true) + } + }) + + this.loaded = !this.loadedImages.length + this.details = detail + + },(error) => { + console.error('getPortfolio ERROR', error) + }).catch((e) => { + console.error('getPortfolio CATCH', e) + }) + } + + back(): void { + this.history.pop() + if(this.history.length > 0) { + const last = this.history[this.history.length - 1] + this.section = last.split('/')[2] + this.id = parseInt(last.split('/')[3]) + this.showDetails(this.section, this.id) + this.location.back() + } else { + this.location.back() + } + } + + + onLoad(index): void { + this.loadedImages[index] = false + this.loaded = this.init && this.loadedImages.every( (val) => !val) + } + + + + + + + + + + + + + openGallery(index: number = 0) { + this.ngxImageGallery.open(index) + } + + // close gallery + closeGallery() { + this.ngxImageGallery.close() + } + + // set new active(visible) image in gallery + newImage(index: number = 0) { + this.ngxImageGallery.setActiveImage(index) + } + + // next image in gallery + nextImage(index: number = 0) { + this.ngxImageGallery.next() + } + + // prev image in gallery + prevImage(index: number = 0) { + this.ngxImageGallery.prev() + } + + /**************************************************/ + + // EVENTS + // callback on gallery opened + galleryOpened(index) { + console.info('Gallery opened at index ', index) + } + + // callback on gallery closed + galleryClosed() { + console.info('Gallery closed.') + } + + // callback on gallery image clicked + galleryImageClicked(index) { + console.info('Gallery image clicked with index ', index) + } + + // callback on gallery image changed + galleryImageChanged(index) { + console.info('Gallery image changed to index ', index) + } + + // callback on user clicked delete button + deleteImage(index) { + console.info('Delete image at index ', index) + } +} diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 3c43e57..dbba668 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,3 +1,21 @@ -
+
+
+ + +
+ +
diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 7281308..83a90d6 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -1,9 +1,157 @@ +@import "../../assets/scss/variables"; .component-header { position: fixed; + display: flex; top: 0; left: 0; - height: 5px; - width: 100%; - background: black; + height: 100vh; + width: 100vw; + background: transparent; + transition: height .5s, background .4s; + z-index: 100; + + .logo-container { + height: 200px; + width: 200px; + max-height: 50vh; + max-width: 90vw; + position: relative; + margin: auto; + padding-top: 20px; + transition: height .6s, width .6s; + z-index: 101; + + .logo { + display: flex; + height: 100%; + width: 100%; + margin: auto; + cursor: pointer; + object-fit: contain; + } + + .circles { + &:before, + &:after { + content: ' '; + position: absolute; + top: -12%; + left: -12%; + display: block; + height: 125%; + width: 125%; + background: $white-alpha; + border-radius: 5px; + z-index: -1; + opacity: .3; + transition: background .6s; + } + + &:after { + border-radius: 20px; + animation: circle2 13s linear infinite; + opacity: .4; + } + &:before { + border-radius: 20px; + animation: circle1 10s linear infinite; + } + } + } + + &.sticky { + height: 0; + background: transparent; + + .logo-container { + height: 100px; + width: 100px; + + .circles { + &:before, + &:after { + //background: $yellow; + } + } + + &.menu-open { + height: 140px; + width: 140px; + .circles { + &:before, + &:after { + background: $white-alpha; + } + } + } + } + } + + .menu { + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; + background: $yellow-alpha; + border-radius: 100px; + transform: scale(0) skew(20deg, 20deg); + transition: transform .5s, border-radius .4s; + z-index: -1; + + .nav { + display: block; + text-align: center; + padding-top: 180px; + height: 100vh; + overflow: hidden; + overflow-y: auto; + + .items { + list-style: none; + padding: 0; + margin: 0; + + .item { + padding: 0; + margin: 10px 0; + font-weight: bold; + font-size: $font-30; + color: $black; + cursor: pointer; + transition: transform .4s; + + &:hover { + transform: scale(1.4); + } + + &.active { + text-decoration: underline; + } + } + } + } + + &.open { + border-radius: 0; + transform: scale(1) skew(0deg, 0deg); + z-index: 100; + } + } + +} + + +@keyframes circle1 { + 0% { transform: translate(-5%, 8%) rotate(10deg) scale(1) skew(20deg, 20deg); } + 75% { transform: translate(10%, -5%) rotate(20deg) scale(1.1) skew(-10deg, -20deg); } + 50% { transform: translate(5%, 0%) rotate(-16deg) scale(.6) skew(-40deg, -10deg); } + 100% { transform: translate(-5%, 8%) rotate(10deg) scale(1) skew(20deg, 20deg); } +} +@keyframes circle2 { + 0% { transform: translate(10%, 5%) rotate(-30deg) scale(1) skew(20deg, 20deg); } + 50% { transform: translate(-5%, 8%) rotate(-17deg) scale(.7) skew(-10deg, -20deg); } + 75% { transform: translate(0%, -10%) rotate(10deg) scale(1.2) skew(-10deg, -50deg); } + 100% { transform: translate(10%, 5%) rotate(-30deg) scale(1) skew(20deg, 20deg); } } diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 7ab4cf7..c4638d6 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,4 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, HostListener, Inject } from '@angular/core' +import { Router, NavigationEnd } from '@angular/router' +import { DOCUMENT } from '@angular/common' @Component({ selector: 'app-header', @@ -7,9 +9,40 @@ import { Component, OnInit } from '@angular/core'; }) export class HeaderComponent implements OnInit { - constructor() { } + public isSticky: boolean = true + public isMenuOpen: boolean = false + public isFirstScroll: boolean = true + + constructor(@Inject(DOCUMENT) private document: Document, private router: Router) { + router.events.subscribe((val) => { + this.isMenuOpen = false + //this.isSticky = true + this.document.body.classList.remove('no-scroll') + }) + } ngOnInit(): void { + //this.isSticky = this.router.url != '/' + } + + @HostListener('window:scroll', ['$event']) + onScroll(event) { + const verticalOffset = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop || 0 + + this.isFirstScroll = this.router.url == '/' + //this.isSticky = this.isFirstScroll ? this.isMenuOpen || verticalOffset > 10 : true + } + + toggleMenu(): void { + this.isMenuOpen = !this.isMenuOpen + if(this.isMenuOpen) { + this.isSticky = true + this.document.body.classList.add('no-scroll') + } else { + this.document.body.classList.remove('no-scroll') + } } } diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html new file mode 100644 index 0000000..2a92674 --- /dev/null +++ b/src/app/home/home.component.html @@ -0,0 +1,31 @@ +
+ + +
+
+
+ loading + +
+ {{item.title}} + {{item.type}} + +
+
+ from + on + {{item.date_from | date}} +
+
+ to {{item.date_to | date}} +
+
+ + {{item.tags}} +
+
+
+
+
+ + diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss new file mode 100644 index 0000000..8c0ef20 --- /dev/null +++ b/src/app/home/home.component.scss @@ -0,0 +1,185 @@ +@import "../../assets/scss/variables"; + +.component-home { + display: flex; + padding-top: 100px; + height: 100vh; + transform: skew(-15deg) rotate(-15deg); + z-index: 1; + + .goto-prev, + .goto-next { + position: absolute; + height: 40px; + width: 60px; + border: none; + background: none; + color: $black; + font-size: $font-40; + appearance: none; + margin: 0; + padding: 5px; + cursor: pointer; + z-index: 2; + + .icon { + &:before { + transform: translate(-50%, -50%); + position: absolute; + top: 50%; + left: 50%; + } + } + } + .goto-prev { + top: 60px; + left: 20px; + } + .goto-next { + top: calc(80vh + 10px); + right: 20px; + .icon { + &:before { + transform: translate(-50%, -50%) rotate(180deg); + } + } + } + + .content { + display: inline-flex; + margin: 0; + margin-left: -50px; + //animation: slide 150s linear infinite; + transition: margin-left .5s; + + @each $width in 1,2,3,4,5,6 { + .slide-#{$width} { + width: #{($width+2)*100}px; + } + } + + .box { + position: relative; + display: flex; + background: $black-alpha2; + border-radius: 5px; + overflow: hidden; + margin: auto 0; + padding: 40px 20px; + height: calc(80vh - 90px); + min-height: 250px; + //max-height: 700px; + cursor: pointer; + transform: skew(-6deg, -6deg) rotate(6deg); + transition: transform .4s, background .4s, opacity .4s; + -webkit-backface-visibility: hidden; + + .image { + position: absolute; + top: 50%; + left: 50%; + height: 100%; + width: 100%; + object-fit: cover; + transform: translate(-50%, -50%); + opacity: .8; + filter: grayscale(100%) contrast(3); + transition: opacity .4s, filter .4s; + -webkit-backface-visibility: hidden; + z-index: 0; + } + + .text { + display: block; + margin: auto; + text-align: center; + transform: translate(0%, 0%); + color: $yellow; + -webkit-backface-visibility: hidden; + z-index: 1; + + .title { + display: block; + font-size: $font-20; + text-transform: uppercase; + font-weight: bold; + } + + .type { + display: block; + font-size: $font-16; + font-weight: bold; + } + + .tags { + display: block; + font-size: $font-12; + text-transform: uppercase; + font-weight: bold; + padding-top: 10px; + } + + .date-container { + display: inline-flex; + flex-wrap: wrap; + + .date-row { + display: block; + width: 100%; + + .date { + display: inline-flex; + margin: auto; + font-size: $font-20; + } + + .date-indication { + margin: 2px 5px auto 5px; + font-size: $font-12; + } + + &:nth-of-type(2) { + margin-top: -12px; + } + } + } + } + + &:hover { + background: $black; + transform: scale(1.4) rotate(14deg) skew(14deg, 0deg); + z-index: 50; + + .image { + filter: grayscale(0%); + opacity: .5; + } + } + } + + &:hover, + &.paused { + //animation: none; + animation-play-state: paused; + } + } +} + + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .component-home { + .goto-prev { + left: 50px; + } + .goto-next { + right: 50px; + } + } +} + + +@keyframes slide { + 0% {margin-left: 0;} + 50% {margin-left: -100%;} + 100% {margin-left: 0;} +} diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts new file mode 100644 index 0000000..490e81b --- /dev/null +++ b/src/app/home/home.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HomeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts new file mode 100644 index 0000000..131f80e --- /dev/null +++ b/src/app/home/home.component.ts @@ -0,0 +1,129 @@ +import { Component, OnInit, ViewChild, ElementRef, Renderer2 } from '@angular/core' +import { Router, NavigationEnd } from '@angular/router' +import { ApisService } from '../services/apis.service' +import { environment } from '../../environments/environment' + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'] +}) +export class HomeComponent implements OnInit { + + @ViewChild('scrollContent') scrollContent: ElementRef + + public basePath = `${environment.BASE_PATH}` + public loaded = false + public init = false + + public homeItems: any = [] + public section: string = 'portfolio' + public paused: boolean = false + private scrollPos: number = 0 + + private swipeCoord?: [number, number] + private swipeTime?: number + + constructor( + private apisService: ApisService, + private router: Router, + private renderer: Renderer2 + ) { } + + ngOnInit(): void { + + this.apisService.getPortfolio(this.section, true).toPromise().then((response) => { + this.homeItems = response.items + + let cnt = 0 + let width = 0 + let tot = 0 + + this.homeItems.forEach((e) => { + e.loading = true + e.width = Math.floor(Math.random()*4)+1 + cnt++ + }) + + },(error) => { + console.error('getPortfolio ERROR', error) + }).catch((e) => { + console.error('getPortfolio CATCH', e) + }) + } + + ngAfterContentInit() { + + this.init = true + + setInterval( () => { + if(!this.paused) { + const scrollWidth = 300 + const scrollPos = parseInt(this.scrollContent.nativeElement.style.marginLeft) || 0 + this.scrollPos = scrollPos - scrollWidth <= -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) ? + -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) : scrollPos - scrollWidth + this.scrollContent.nativeElement.style.marginLeft = this.scrollPos + 'px' + } + },2000) + } + + showDetails(id, title = ''): void { + const section = this.section == 'exhibitions' ? 'exhibitions' : 'works' + this.router.navigate([`/detail/${section}/${id}/${title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '_')}`]) + } + + onLoad(id): void { + this.homeItems.filter(item => item.id == id)[0].loading = false + this.loaded = this.init && this.homeItems.every( (val) => !val.loading) + } + + scroll(dir): void { + const scrollWidth = document.body.clientWidth / 3 + const scrollPos = parseInt(this.scrollContent.nativeElement.style.marginLeft) || 0 + this.paused = true + + switch(dir) { + case 'prev': + this.scrollPos = scrollPos + scrollWidth >= 0 ? 0 : scrollPos + scrollWidth + break; + case 'next': + this.scrollPos = scrollPos - scrollWidth <= -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) ? + -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) : scrollPos - scrollWidth + break; + } + this.scrollContent.nativeElement.style.marginLeft = this.scrollPos + 'px' + } + + swipe(e: TouchEvent, when: string): void { + const coord: [number, number] = [e.changedTouches[0].clientX, e.changedTouches[0].clientY] + const time = new Date().getTime() + const scrollWidth = document.body.clientWidth + const scrollPos = parseInt(this.scrollContent.nativeElement.style.marginLeft) || 0 + + this.paused = true + + if (when === 'start') { + this.swipeCoord = coord + this.swipeTime = time + } else if (when === 'end') { + const direction = [coord[0] - this.swipeCoord[0], coord[1] - this.swipeCoord[1]] + const duration = time - this.swipeTime + + if (duration < 1000 + && Math.abs(direction[0]) > 30 + && Math.abs(direction[0]) > Math.abs(direction[1] * 3)) { + const swipe = direction[0] < 0 ? 'next' : 'prev' + switch(swipe) { + case 'prev': + this.scrollPos = scrollPos + scrollWidth >= 0 ? 0 : scrollPos + scrollWidth + break; + case 'next': + this.scrollPos = scrollPos - scrollWidth <= -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) ? + -(this.scrollContent.nativeElement.offsetWidth - document.body.clientWidth) : scrollPos - scrollWidth + break; + } + this.scrollContent.nativeElement.style.marginLeft = this.scrollPos + 'px' + } + } + } +} diff --git a/src/app/portfolio/portfolio.component.html b/src/app/portfolio/portfolio.component.html new file mode 100644 index 0000000..dc8c0fd --- /dev/null +++ b/src/app/portfolio/portfolio.component.html @@ -0,0 +1,29 @@ +
+
+
+
+ loading + +
+ {{item.title}} + {{item.type}} + +
+
+ from + on + {{item.date_from | date}} +
+
+ to {{item.date_to | date}} +
+
+ + {{item.tags}} +
+
+
+
+
+ + diff --git a/src/app/portfolio/portfolio.component.scss b/src/app/portfolio/portfolio.component.scss new file mode 100644 index 0000000..ab9be70 --- /dev/null +++ b/src/app/portfolio/portfolio.component.scss @@ -0,0 +1,134 @@ +@import "../../assets/scss/variables"; + +.component-portfolio { + padding: 160px 0 80px 0; + + .row-container { + position: relative; + transform: skew(-2deg, -2deg) rotate(-2deg); + width: calc(100% - 50px); + max-width: 1400px; + margin: auto; + z-index: 1; + + .box { + position: relative; + display: flex; + border-radius: 4px; + overflow: hidden; + background: $black-alpha; + margin: 6px; + padding: 40px 20px; + min-height: 250px; + cursor: pointer; + transform: skew(6deg, -6deg) rotate(6deg); + transition: transform .4s, background .4s, box-shadow .4s; + -webkit-backface-visibility: hidden; + + .image { + position: absolute; + top: 50%; + left: 50%; + height: 100%; + width: 100%; + object-fit: cover; + transform: translate(-50%, -50%); + opacity: .9; + filter: grayscale(100%) brightness(.4); + transition: opacity .4s, filter .4s; + -webkit-backface-visibility: hidden; + z-index: 0; + } + + .loader { + position: absolute; + top: 50%; + left: 50%; + height: 100%; + width: 100%; + object-fit: cover; + transform: translate(-50%, -50%); + z-index: 0; + } + + .text { + display: block; + margin: auto; + text-align: center; + transform: translate(0%, 0%); + color: $yellow; + //transition: color .4s; + -webkit-backface-visibility: hidden; + z-index: 1; + + .title { + display: block; + font-size: $font-20; + text-transform: uppercase; + font-weight: bold; + } + + .type { + display: block; + font-size: $font-16; + font-weight: bold; + } + + .tags { + display: block; + font-size: $font-12; + text-transform: uppercase; + font-weight: bold; + padding-top: 10px; + } + + .date-container { + display: inline-flex; + flex-wrap: wrap; + + .date-row { + display: block; + width: 100%; + + .date { + display: inline-flex; + margin: auto; + font-size: $font-20; + } + + .date-indication { + margin: 2px 5px auto 5px; + font-size: $font-12; + } + + &:nth-of-type(2) { + margin-top: -12px; + } + } + } + + } + + &:hover { + box-shadow: 0 0 20px $white-alpha2; + background: $black; + z-index: 50; + + transform: scale(1.4) rotate(4deg) skew(3deg); + + .image { + filter: grayscale(0%) brightness(1); + opacity: .7; + } + } + } + } +} + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .component-portfolio { + .row-container { + width: calc(100% - 100px); + } + } +} diff --git a/src/app/portfolio/portfolio.component.spec.ts b/src/app/portfolio/portfolio.component.spec.ts new file mode 100644 index 0000000..d16d6bc --- /dev/null +++ b/src/app/portfolio/portfolio.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PortfolioComponent } from './portfolio.component'; + +describe('PortfolioComponent', () => { + let component: PortfolioComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PortfolioComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PortfolioComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/portfolio/portfolio.component.ts b/src/app/portfolio/portfolio.component.ts new file mode 100644 index 0000000..f7f2c66 --- /dev/null +++ b/src/app/portfolio/portfolio.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit } from '@angular/core' +import { Router, NavigationEnd } from '@angular/router' +import { ApisService } from '../services/apis.service' +import { environment } from '../../environments/environment' + +@Component({ + selector: 'app-portfolio', + templateUrl: './portfolio.component.html', + styleUrls: ['./portfolio.component.scss'] +}) +export class PortfolioComponent implements OnInit { + + public basePath = `${environment.BASE_PATH}` + public loaded = false + public init = false + + public portfolioItems: any = [] + public section: string = '' + + constructor( + private apisService: ApisService, + private router: Router) + { } + + ngOnInit(): void { + this.section = this.router.url.split('/')[1] + + this.apisService.getPortfolio(this.section).toPromise().then((response) => { + this.portfolioItems = response.items + + let cnt = 0 + let width = 0 + let tot = 0 + + this.portfolioItems.forEach((e) => { + e.loading = true + + switch (cnt) { + case 0: + width = Math.floor(Math.random()*3)+3 + tot = width + cnt++ + break; + case 1: + width = Math.floor(Math.random()*3)+3 + cnt++ + if(tot + width > 9) { + width = 12 - tot + tot = 0 + cnt = 0 + } else { + tot = tot + width + } + break; + case 2: + width = 12 - tot + tot = 0 + cnt = 0 + break; + } + e.width = width + }) + },(error) => { + console.error('getPortfolio ERROR', error) + }).catch((e) => { + console.error('getPortfolio CATCH', e) + }) + } + + ngAfterContentInit() { + this.init = true + } + + showDetails(id, title = ''): void { + const section = this.section == 'exhibitions' ? 'exhibitions' : 'works' + this.router.navigate([`/detail/${section}/${id}/${title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '_')}`]) + } + + onLoad(id): void { + this.portfolioItems.filter(item => item.id == id)[0].loading = false + this.loaded = this.init && this.portfolioItems.every( (val) => !val.loading) + } + +} diff --git a/src/app/services/apis.service.spec.ts b/src/app/services/apis.service.spec.ts new file mode 100644 index 0000000..a952a5c --- /dev/null +++ b/src/app/services/apis.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ApisService } from './apis.service'; + +describe('ApisService', () => { + let service: ApisService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ApisService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/apis.service.ts b/src/app/services/apis.service.ts new file mode 100644 index 0000000..47887ba --- /dev/null +++ b/src/app/services/apis.service.ts @@ -0,0 +1,77 @@ +import { Injectable } from '@angular/core' +import { HttpClient, HttpHeaders, HttpParams, HttpRequest } from '@angular/common/http' +import { Observable, Subject, throwError } from 'rxjs' +import { catchError } from 'rxjs/operators' +import { BaseService } from './base-service' +import { environment } from '../../environments/environment' + +@Injectable({ + providedIn: 'root' +}) +export class ApisService extends BaseService { + + private restApi = `${environment.API_URL}` + + constructor(private http: HttpClient) { + super() + } + + getPortfolio(section, randon = false): Observable { + let urlApi = `${this.restApi}?query=${section}&random=${randon}` + return this.http.get(urlApi).pipe( + catchError(this.handleError) + ) + } + + getDetails(section, id): Observable { + let urlApi = `${this.restApi}?query=detail&type=${section}&id=${id}` + return this.http.get(urlApi).pipe( + catchError(this.handleError) + ) + } + + // ADMIN SERVICES + uploadImage(body): Observable { + let urlApi = `${this.restApi}upload.php` + return this.http.post(urlApi, body).pipe( + catchError(this.handleError) + ) + } + + removeImage(body): Observable { + let urlApi = `${this.restApi}remove.php` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + saveWork(body): Observable { + let urlApi = `${this.restApi}work.php?act=save` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + deleteWork(body): Observable { + let urlApi = `${this.restApi}work.php?act=delete` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + saveExhibition(body): Observable { + let urlApi = `${this.restApi}exhibition.php?act=save` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + deleteExhibition(body): Observable { + let urlApi = `${this.restApi}exhibition.php?act=delete` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + +} diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts new file mode 100644 index 0000000..f1251ca --- /dev/null +++ b/src/app/services/auth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts new file mode 100644 index 0000000..3ee8d3c --- /dev/null +++ b/src/app/services/auth.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core' +import { HttpClient, HttpHeaders, HttpParams, HttpRequest } from '@angular/common/http' +import { Observable, Subject, throwError } from 'rxjs' +import { catchError } from 'rxjs/operators' +import { BaseService } from './base-service' +import { environment } from '../../environments/environment' + +@Injectable({ + providedIn: 'root' +}) +export class AuthService extends BaseService { + + private restApi = `${environment.API_URL}` + + constructor(private http: HttpClient) { + super() + } + + login(body): Observable { + let urlApi = `${this.restApi}auth.php?act=login` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + + authCheck(body): Observable { + let urlApi = `${this.restApi}auth.php?act=check` + return this.http.post(urlApi, JSON.stringify(body)).pipe( + catchError(this.handleError) + ) + } + +} diff --git a/src/app/services/base-service.ts b/src/app/services/base-service.ts new file mode 100644 index 0000000..50813ce --- /dev/null +++ b/src/app/services/base-service.ts @@ -0,0 +1,16 @@ +import { HttpErrorResponse } from "@angular/common/http" +import { throwError } from "rxjs" +import { ParseXML } from "./parse-xml" + +export class BaseService { + + constructor() { } + + protected handleError(error: HttpErrorResponse) { + if(error.error instanceof ErrorEvent) { + console.error('An error occurred:', error.error.message) + } + + return throwError( ParseXML.getXMLResponseMessage(error.error) ) + } +} diff --git a/src/app/services/parse-xml.ts b/src/app/services/parse-xml.ts new file mode 100644 index 0000000..781bd33 --- /dev/null +++ b/src/app/services/parse-xml.ts @@ -0,0 +1,13 @@ +export class ParseXML { + constructor() {} + + public sanitize(str: string): string { + let sanitizeString = encodeURIComponent(str).replace(/%0A/g, '') + return decodeURIComponent(sanitizeString) + } + + public static getXMLResponseMessage(responseBody: string): string { + let parseXMLClass = new ParseXML() + return parseXMLClass.sanitize(responseBody).match(/(.*?)<\/Message>/g)[0].replace(/<[^>]+>/g, '') + } +} diff --git a/src/app/spinner/spinner.component.html b/src/app/spinner/spinner.component.html new file mode 100644 index 0000000..91af365 --- /dev/null +++ b/src/app/spinner/spinner.component.html @@ -0,0 +1,2 @@ + +
diff --git a/src/app/spinner/spinner.component.scss b/src/app/spinner/spinner.component.scss new file mode 100644 index 0000000..ded7d41 --- /dev/null +++ b/src/app/spinner/spinner.component.scss @@ -0,0 +1,15 @@ +@import "../../assets/scss/variables"; + +.spinner { + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + background: $yellow-alpha; + background-image: url('/assets/images/loader.svg'); + background-size: 200px 200px; + background-repeat: no-repeat; + background-position: center center; + z-index: 100; +} diff --git a/src/app/spinner/spinner.component.ts b/src/app/spinner/spinner.component.ts new file mode 100644 index 0000000..2def854 --- /dev/null +++ b/src/app/spinner/spinner.component.ts @@ -0,0 +1,56 @@ +import { Component, OnInit, Input, ViewChild, ElementRef, SimpleChanges } from '@angular/core' + +@Component({ + selector: 'app-spinner', + templateUrl: './spinner.component.html', + styleUrls: ['./spinner.component.scss'] +}) + +export class SpinnerComponent implements OnInit { + + @ViewChild('spinner') spinner: ElementRef + @Input() show: boolean = false + + ngOnInit(): void { + } + + ngOnChanges(changes: SimpleChanges) { + if(changes.show && changes.show.firstChange) { + this.loader(changes.show.currentValue ? 'show' : 'hide') + } + } + + loader(action) { + let op = 0 + let timer = null + + switch(action) { + case 'show': + op = 1 + timer = setInterval(() => { + if(op <= 0.1){ + clearInterval(timer) + this.spinner.nativeElement.style.display = 'none' + } + this.spinner.nativeElement.style.opacity = op + this.spinner.nativeElement.style.filter = 'alpha(opacity=' + op * 100 + ")" + op -= op * 0.1 + }, 20) + + break + case 'hide': + op = 0.1 + this.spinner.nativeElement.style.display = 'block' + timer = setInterval(() => { + if(op >= 1){ + clearInterval(timer) + } + this.spinner.nativeElement.style.opacity = op + this.spinner.nativeElement.style.filter = 'alpha(opacity=' + op * 100 + ")" + op += op * 0.1 + }, 20) + + break + } + } +} diff --git a/src/assets/fonts/icomoon.eot b/src/assets/fonts/icomoon.eot new file mode 100644 index 0000000..2f6e5ad Binary files /dev/null and b/src/assets/fonts/icomoon.eot differ diff --git a/src/assets/fonts/icomoon.svg b/src/assets/fonts/icomoon.svg new file mode 100644 index 0000000..eb5888b --- /dev/null +++ b/src/assets/fonts/icomoon.svg @@ -0,0 +1,38 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/fonts/icomoon.ttf b/src/assets/fonts/icomoon.ttf new file mode 100644 index 0000000..f258805 Binary files /dev/null and b/src/assets/fonts/icomoon.ttf differ diff --git a/src/assets/fonts/icomoon.woff b/src/assets/fonts/icomoon.woff new file mode 100644 index 0000000..1666da4 Binary files /dev/null and b/src/assets/fonts/icomoon.woff differ diff --git a/src/assets/fonts/selection.json b/src/assets/fonts/selection.json new file mode 100644 index 0000000..c8a7fb2 --- /dev/null +++ b/src/assets/fonts/selection.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M981.333 512c0-129.579-52.565-246.997-137.472-331.861s-202.283-137.472-331.861-137.472-246.997 52.565-331.861 137.472-137.472 202.283-137.472 331.861 52.565 246.997 137.472 331.861 202.283 137.472 331.861 137.472 246.997-52.565 331.861-137.472 137.472-202.283 137.472-331.861zM896 512c0 106.069-42.923 201.984-112.469 271.531s-165.461 112.469-271.531 112.469-201.984-42.923-271.531-112.469-112.469-165.461-112.469-271.531 42.923-201.984 112.469-271.531 165.461-112.469 271.531-112.469 201.984 42.923 271.531 112.469 112.469 165.461 112.469 271.531zM469.333 341.333v170.667c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-170.667c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667zM512 725.333c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667-42.667 19.115-42.667 42.667 19.115 42.667 42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["alert-circle"],"grid":24},"attrs":[],"properties":{"id":3,"order":59,"prevSize":24,"code":59652,"name":"alert-circle"},"setIdx":0,"setId":1,"iconIdx":2},{"icon":{"paths":["M475.648 186.624c3.115-5.248 7.893-10.325 14.251-14.165 10.069-6.101 21.589-7.595 32.256-4.949s20.224 9.216 26.197 19.115l361.216 603.008c2.987 5.12 5.077 11.435 5.461 18.517-0.64 15.701-5.077 25.216-12.075 32.384-7.68 7.851-18.219 12.715-29.568 12.843l-722.645-0.043c-6.485-0.043-13.696-1.749-20.523-5.717-10.197-5.888-17.024-15.317-19.883-25.899s-1.621-22.144 3.925-31.787zM402.432 142.763l-361.387 603.307c-18.005 31.189-21.589 66.133-13.141 97.707s29.013 60.075 59.648 77.739c19.797 11.435 41.643 17.067 62.933 17.152h722.901c35.797-0.384 67.712-15.104 90.581-38.485s36.864-55.595 36.48-90.923c-0.256-22.869-6.528-44.544-17.323-62.891l-361.557-603.605c-18.432-30.421-47.36-50.389-79.104-58.155s-66.603-3.456-96.811 14.891c-18.304 11.093-33.067 26.24-43.179 43.264zM469.333 384v170.667c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-170.667c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667zM512 768c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667-42.667 19.115-42.667 42.667 19.115 42.667 42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["alert-triangle"],"grid":24},"attrs":[],"properties":{"id":5,"order":58,"prevSize":24,"code":59653,"name":"alert-triangle"},"setIdx":0,"setId":1,"iconIdx":4},{"icon":{"paths":["M542.165 780.501l-225.835-225.835h494.336c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667h-494.336l225.835-225.835c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0l-298.667 298.667c-4.096 4.096-7.168 8.789-9.259 13.824-2.176 5.205-3.243 10.795-3.243 16.341 0 10.923 4.181 21.845 12.501 30.165l298.667 298.667c16.683 16.683 43.691 16.683 60.331 0s16.683-43.691 0-60.331z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["arrow-left"],"grid":24},"attrs":[],"properties":{"id":17,"order":52,"prevSize":24,"code":59649,"name":"arrow-left"},"setIdx":0,"setId":1,"iconIdx":16},{"icon":{"paths":["M823.168 225.835l-439.168 439.168-183.168-183.168c-16.683-16.683-43.691-16.683-60.331 0s-16.683 43.691 0 60.331l213.333 213.333c16.683 16.683 43.691 16.683 60.331 0l469.333-469.333c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["check"],"grid":24},"attrs":[],"properties":{"id":44,"order":57,"prevSize":24,"code":59654,"name":"check"},"setIdx":0,"setId":1,"iconIdx":43},{"icon":{"paths":["M225.835 414.165l256 256c16.683 16.683 43.691 16.683 60.331 0l256-256c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0l-225.835 225.835-225.835-225.835c-16.683-16.683-43.691-16.683-60.331 0s-16.683 43.691 0 60.331z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["chevron-down"],"grid":24},"attrs":[],"properties":{"id":47,"order":56,"prevSize":24,"code":59650,"name":"chevron-down"},"setIdx":0,"setId":1,"iconIdx":46},{"icon":{"paths":["M170.667 213.333h298.667c35.371 0 67.285 14.293 90.496 37.504s37.504 55.125 37.504 90.496v409.003l-140.501-140.501c-16.683-16.683-43.691-16.683-60.331 0s-16.683 43.691 0 60.331l213.333 213.333c3.925 3.925 8.619 7.083 13.824 9.259 10.453 4.309 22.229 4.309 32.683 0 5.035-2.091 9.728-5.163 13.824-9.259l213.333-213.333c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0l-140.501 140.501v-409.003c0-58.88-23.936-112.299-62.464-150.869s-91.989-62.464-150.869-62.464h-298.667c-23.552 0-42.667 19.115-42.667 42.667s19.115 42.667 42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["corner-right-down"],"grid":24},"attrs":[],"properties":{"id":77,"order":61,"prevSize":24,"code":59655,"name":"corner-right-down"},"setIdx":0,"setId":1,"iconIdx":76},{"icon":{"paths":["M896 213.333c11.776 0 22.4 4.736 30.165 12.501s12.501 18.389 12.501 30.165v512c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-535.296l-261.333-298.667 261.333-298.667zM896 128h-554.667c-12.8 0-24.235 5.632-32.128 14.549l-298.667 341.333c-14.208 16.213-13.909 40.192 0 56.192l298.667 341.333c8.448 9.643 20.224 14.549 32.128 14.592h554.667c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-512c0-35.328-14.379-67.413-37.504-90.496s-55.168-37.504-90.496-37.504zM481.835 414.165l97.835 97.835-97.835 97.835c-16.683 16.683-16.683 43.691 0 60.331s43.691 16.683 60.331 0l97.835-97.835 97.835 97.835c16.683 16.683 43.691 16.683 60.331 0s16.683-43.691 0-60.331l-97.835-97.835 97.835-97.835c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0l-97.835 97.835-97.835-97.835c-16.683-16.683-43.691-16.683-60.331 0s-16.683 43.691 0 60.331z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["delete"],"grid":24},"attrs":[],"properties":{"id":86,"order":54,"prevSize":24,"code":59651,"name":"delete"},"setIdx":0,"setId":1,"iconIdx":85},{"icon":{"paths":["M469.333 128h-298.667c-35.328 0-67.413 14.379-90.496 37.504s-37.504 55.168-37.504 90.496v597.333c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h597.333c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-298.667c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667v298.667c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-597.333c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-597.333c0-11.776 4.736-22.4 12.501-30.165s18.389-12.501 30.165-12.501h298.667c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667zM759.168 76.501l-405.333 405.333c-5.205 5.163-9.259 11.947-11.221 19.84l-42.667 170.667c-1.664 6.4-1.792 13.568 0 20.693 5.717 22.869 28.885 36.779 51.755 31.061l170.667-42.667c7.125-1.749 14.080-5.504 19.84-11.221l405.333-405.333c25.984-25.984 38.997-60.16 38.997-94.165s-13.013-68.181-38.997-94.165-60.203-39.040-94.208-39.040-68.181 13.013-94.165 38.997zM819.499 136.832c9.344-9.344 21.504-13.995 33.835-13.995s24.491 4.651 33.835 13.995 13.995 21.504 13.995 33.835-4.651 24.491-13.995 33.835l-396.971 396.971-90.197 22.571 22.571-90.197z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["edit"],"grid":24},"attrs":[],"properties":{"id":92,"order":60,"prevSize":24,"code":59656,"name":"edit"},"setIdx":0,"setId":1,"iconIdx":91},{"icon":{"paths":["M213.333 85.333c-35.328 0-67.413 14.379-90.496 37.504s-37.504 55.168-37.504 90.496v597.333c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h597.333c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-597.333c0-35.328-14.379-67.413-37.504-90.496s-55.168-37.504-90.496-37.504zM213.333 170.667h597.333c11.776 0 22.4 4.736 30.165 12.501s12.501 18.389 12.501 30.165v597.333c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-597.333c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-597.333c0-11.776 4.736-22.4 12.501-30.165s18.389-12.501 30.165-12.501zM341.333 554.667h341.333c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667h-341.333c-23.552 0-42.667 19.115-42.667 42.667s19.115 42.667 42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["minus-square"],"grid":24},"attrs":[],"properties":{"id":162,"order":65,"prevSize":24,"code":59657,"name":"minus-square"},"setIdx":0,"setId":1,"iconIdx":161},{"icon":{"paths":["M213.333 85.333c-35.328 0-67.413 14.379-90.496 37.504s-37.504 55.168-37.504 90.496v597.333c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h597.333c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-597.333c0-35.328-14.379-67.413-37.504-90.496s-55.168-37.504-90.496-37.504zM213.333 170.667h597.333c11.776 0 22.4 4.736 30.165 12.501s12.501 18.389 12.501 30.165v597.333c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-597.333c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-597.333c0-11.776 4.736-22.4 12.501-30.165s18.389-12.501 30.165-12.501zM341.333 554.667h128v128c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-128h128c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667h-128v-128c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667v128h-128c-23.552 0-42.667 19.115-42.667 42.667s19.115 42.667 42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["plus-square"],"grid":24},"attrs":[],"properties":{"id":191,"order":64,"prevSize":24,"code":59658,"name":"plus-square"},"setIdx":0,"setId":1,"iconIdx":190},{"icon":{"paths":["M128 512v341.333c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h512c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-341.333c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667v341.333c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-512c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-341.333c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667zM469.333 188.331v451.669c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-451.669l97.835 97.835c16.683 16.683 43.691 16.683 60.331 0s16.683-43.691 0-60.331l-170.667-170.667c-3.925-3.925-8.619-7.083-13.824-9.259-10.453-4.309-22.229-4.309-32.683 0-5.035 2.091-9.728 5.163-13.824 9.259l-170.667 170.667c-16.683 16.683-16.683 43.691 0 60.331s43.691 16.683 60.331 0z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["share"],"grid":24},"attrs":[],"properties":{"id":209,"order":66,"prevSize":24,"code":59659,"name":"share"},"setIdx":0,"setId":1,"iconIdx":208},{"icon":{"paths":["M768 298.667v554.667c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-426.667c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-554.667zM725.333 213.333v-42.667c0-35.328-14.379-67.413-37.504-90.496s-55.168-37.504-90.496-37.504h-170.667c-35.328 0-67.413 14.379-90.496 37.504s-37.504 55.168-37.504 90.496v42.667h-170.667c-23.552 0-42.667 19.115-42.667 42.667s19.115 42.667 42.667 42.667h42.667v554.667c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h426.667c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-554.667h42.667c23.552 0 42.667-19.115 42.667-42.667s-19.115-42.667-42.667-42.667zM384 213.333v-42.667c0-11.776 4.736-22.4 12.501-30.165s18.389-12.501 30.165-12.501h170.667c11.776 0 22.4 4.736 30.165 12.501s12.501 18.389 12.501 30.165v42.667zM384 469.333v256c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-256c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667zM554.667 469.333v256c0 23.552 19.115 42.667 42.667 42.667s42.667-19.115 42.667-42.667v-256c0-23.552-19.115-42.667-42.667-42.667s-42.667 19.115-42.667 42.667z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["trash-2"],"grid":24},"attrs":[],"properties":{"id":242,"order":67,"prevSize":24,"code":59660,"name":"trash-2"},"setIdx":0,"setId":1,"iconIdx":241},{"icon":{"paths":["M213.333 85.333c-35.328 0-67.413 14.379-90.496 37.504s-37.504 55.168-37.504 90.496v597.333c0 35.328 14.379 67.413 37.504 90.496s55.168 37.504 90.496 37.504h597.333c35.328 0 67.413-14.379 90.496-37.504s37.504-55.168 37.504-90.496v-597.333c0-35.328-14.379-67.413-37.504-90.496s-55.168-37.504-90.496-37.504zM213.333 170.667h597.333c11.776 0 22.4 4.736 30.165 12.501s12.501 18.389 12.501 30.165v597.333c0 11.776-4.736 22.4-12.501 30.165s-18.389 12.501-30.165 12.501h-597.333c-11.776 0-22.4-4.736-30.165-12.501s-12.501-18.389-12.501-30.165v-597.333c0-11.776 4.736-22.4 12.501-30.165s18.389-12.501 30.165-12.501zM609.835 353.835l-97.835 97.835-97.835-97.835c-16.683-16.683-43.691-16.683-60.331 0s-16.683 43.691 0 60.331l97.835 97.835-97.835 97.835c-16.683 16.683-16.683 43.691 0 60.331s43.691 16.683 60.331 0l97.835-97.835 97.835 97.835c16.683 16.683 43.691 16.683 60.331 0s16.683-43.691 0-60.331l-97.835-97.835 97.835-97.835c16.683-16.683 16.683-43.691 0-60.331s-43.691-16.683-60.331 0z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["x-square"],"grid":24},"attrs":[],"properties":{"id":277,"order":68,"prevSize":24,"code":59661,"name":"x-square"},"setIdx":0,"setId":1,"iconIdx":276},{"icon":{"paths":["M950.857 859.429v-438.857c-12 13.714-25.143 26.286-39.429 37.714-81.714 62.857-164 126.857-243.429 193.143-42.857 36-96 80-155.429 80h-1.143c-59.429 0-112.571-44-155.429-80-79.429-66.286-161.714-130.286-243.429-193.143-14.286-11.429-27.429-24-39.429-37.714v438.857c0 9.714 8.571 18.286 18.286 18.286h841.143c9.714 0 18.286-8.571 18.286-18.286zM950.857 258.857c0-14.286 3.429-39.429-18.286-39.429h-841.143c-9.714 0-18.286 8.571-18.286 18.286 0 65.143 32.571 121.714 84 162.286 76.571 60 153.143 120.571 229.143 181.143 30.286 24.571 85.143 77.143 125.143 77.143h1.143c40 0 94.857-52.571 125.143-77.143 76-60.571 152.571-121.143 229.143-181.143 37.143-29.143 84-92.571 84-141.143zM1024 237.714v621.714c0 50.286-41.143 91.429-91.429 91.429h-841.143c-50.286 0-91.429-41.143-91.429-91.429v-621.714c0-50.286 41.143-91.429 91.429-91.429h841.143c50.286 0 91.429 41.143 91.429 91.429z"],"width":1024,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["envelope-o"],"defaultCode":61443,"grid":14},"attrs":[],"properties":{"name":"envelope-o","id":0,"order":47,"prevSize":28,"code":61443},"setIdx":1,"setId":0,"iconIdx":0},{"icon":{"paths":["M731.429 348.571c-21.714 9.714-44.571 16-69.143 19.429 25.143-14.857 44-38.857 53.143-66.857-23.429 13.714-49.143 24-76.571 29.143-21.714-23.429-53.143-37.714-87.429-37.714-66.286 0-120 53.714-120 120 0 9.143 0.571 18.857 2.857 27.429-100-5.143-188.571-52.571-248-125.714-10.286 17.714-16.571 38.857-16.571 60.571 0 41.714 19.429 78.286 52 100-20-0.571-38.857-6.286-57.143-14.857v1.143c0 58.286 44 106.857 98.857 117.714-10.286 2.857-18.286 4.571-29.143 4.571-7.429 0-14.857-1.143-22.286-2.286 15.429 47.429 59.429 82.286 112 83.429-41.143 32-92.571 51.429-149.143 51.429-9.714 0-19.429-0.571-28.571-1.714 53.143 33.714 116 53.714 184 53.714 220.571 0 341.714-182.857 341.714-341.714 0-5.143 0-10.286-0.571-15.429 23.429-16.571 44-37.714 60-62.286zM877.714 237.714v548.571c0 90.857-73.714 164.571-164.571 164.571h-548.571c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571c90.857 0 164.571 73.714 164.571 164.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["twitter-square"],"defaultCode":61569,"grid":14},"attrs":[],"properties":{"name":"twitter-square","id":1,"order":31,"prevSize":28,"code":61569},"setIdx":1,"setId":0,"iconIdx":1},{"icon":{"paths":["M713.143 73.143c90.857 0 164.571 73.714 164.571 164.571v548.571c0 90.857-73.714 164.571-164.571 164.571h-107.429v-340h113.714l17.143-132.571h-130.857v-84.571c0-38.286 10.286-64 65.714-64l69.714-0.571v-118.286c-12-1.714-53.714-5.143-101.714-5.143-101.143 0-170.857 61.714-170.857 174.857v97.714h-114.286v132.571h114.286v340h-304c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["facebook-square"],"defaultCode":61570,"grid":14},"attrs":[],"properties":{"name":"facebook-square","id":2,"order":30,"prevSize":28,"code":61570},"setIdx":1,"setId":0,"iconIdx":2},{"icon":{"paths":["M804.571 708.571c0 20.571-9.143 60.571-17.714 79.429-12 28-44 46.286-69.714 60.571-33.714 18.286-68 29.143-106.286 29.143-53.143 0-101.143-21.714-149.714-39.429-34.857-12.571-68.571-28-100-47.429-97.143-60-214.286-177.143-274.286-274.286-19.429-31.429-34.857-65.143-47.429-100-17.714-48.571-39.429-96.571-39.429-149.714 0-38.286 10.857-72.571 29.143-106.286 14.286-25.714 32.571-57.714 60.571-69.714 18.857-8.571 58.857-17.714 79.429-17.714 4 0 8 0 12 1.714 12 4 24.571 32 30.286 43.429 18.286 32.571 36 65.714 54.857 97.714 9.143 14.857 26.286 33.143 26.286 50.857 0 34.857-103.429 85.714-103.429 116.571 0 15.429 14.286 35.429 22.286 49.143 57.714 104 129.714 176 233.714 233.714 13.714 8 33.714 22.286 49.143 22.286 30.857 0 81.714-103.429 116.571-103.429 17.714 0 36 17.143 50.857 26.286 32 18.857 65.143 36.571 97.714 54.857 11.429 5.714 39.429 18.286 43.429 30.286 1.714 4 1.714 8 1.714 12z"],"width":804.5714285714286,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["phone"],"defaultCode":61589,"grid":14},"attrs":[],"properties":{"name":"phone","id":3,"order":43,"prevSize":28,"code":61589},"setIdx":1,"setId":0,"iconIdx":3},{"icon":{"paths":["M731.429 681.714c0-2.857 0-6.286-1.143-9.143-3.429-10.286-86.857-52.571-102.857-61.714-10.857-6.286-24-18.857-37.143-18.857-25.143 0-62.286 74.857-84.571 74.857-11.429 0-25.714-10.286-36-16-75.429-42.286-127.429-94.286-169.714-169.714-5.714-10.286-16-24.571-16-36 0-22.286 74.857-59.429 74.857-84.571 0-13.143-12.571-26.286-18.857-37.143-9.143-16-51.429-99.429-61.714-102.857-2.857-1.143-6.286-1.143-9.143-1.143-14.857 0-44 6.857-57.714 12.571-37.714 17.143-65.143 89.143-65.143 128.571 0 38.286 15.429 73.143 28.571 108.571 45.714 125.143 181.714 261.143 306.857 306.857 35.429 13.143 70.286 28.571 108.571 28.571 39.429 0 111.429-27.429 128.571-65.143 5.714-13.714 12.571-42.857 12.571-57.714zM877.714 237.714v548.571c0 90.857-73.714 164.571-164.571 164.571h-548.571c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571c90.857 0 164.571 73.714 164.571 164.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["phone-square"],"defaultCode":61592,"grid":14},"attrs":[],"properties":{"name":"phone-square","id":4,"order":35,"prevSize":28,"code":61592},"setIdx":1,"setId":0,"iconIdx":4},{"icon":{"paths":["M925.714 233.143c-25.143 36.571-56.571 69.143-92.571 95.429 0.571 8 0.571 16 0.571 24 0 244-185.714 525.143-525.143 525.143-104.571 0-201.714-30.286-283.429-82.857 14.857 1.714 29.143 2.286 44.571 2.286 86.286 0 165.714-29.143 229.143-78.857-81.143-1.714-149.143-54.857-172.571-128 11.429 1.714 22.857 2.857 34.857 2.857 16.571 0 33.143-2.286 48.571-6.286-84.571-17.143-148-91.429-148-181.143v-2.286c24.571 13.714 53.143 22.286 83.429 23.429-49.714-33.143-82.286-89.714-82.286-153.714 0-34.286 9.143-65.714 25.143-93.143 90.857 112 227.429 185.143 380.571 193.143-2.857-13.714-4.571-28-4.571-42.286 0-101.714 82.286-184.571 184.571-184.571 53.143 0 101.143 22.286 134.857 58.286 41.714-8 81.714-23.429 117.143-44.571-13.714 42.857-42.857 78.857-81.143 101.714 37.143-4 73.143-14.286 106.286-28.571z"],"width":950.8571428571428,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["twitter"],"defaultCode":61593,"grid":14},"attrs":[],"properties":{"name":"twitter","id":5,"order":45,"prevSize":28,"code":61593},"setIdx":1,"setId":0,"iconIdx":5},{"icon":{"paths":["M548 6.857v150.857h-89.714c-70.286 0-83.429 33.714-83.429 82.286v108h167.429l-22.286 169.143h-145.143v433.714h-174.857v-433.714h-145.714v-169.143h145.714v-124.571c0-144.571 88.571-223.429 217.714-223.429 61.714 0 114.857 4.571 130.286 6.857z"],"width":602.2582857142856,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["facebook","facebook-f"],"defaultCode":61594,"grid":14},"attrs":[],"properties":{"name":"facebook, facebook-f","id":6,"order":44,"prevSize":28,"code":61594},"setIdx":1,"setId":0,"iconIdx":6},{"icon":{"paths":["M1024 405.714v453.714c0 50.286-41.143 91.429-91.429 91.429h-841.143c-50.286 0-91.429-41.143-91.429-91.429v-453.714c17.143 18.857 36.571 35.429 57.714 49.714 94.857 64.571 190.857 129.143 284 197.143 48 35.429 107.429 78.857 169.714 78.857h1.143c62.286 0 121.714-43.429 169.714-78.857 93.143-67.429 189.143-132.571 284.571-197.143 20.571-14.286 40-30.857 57.143-49.714zM1024 237.714c0 64-47.429 121.714-97.714 156.571-89.143 61.714-178.857 123.429-267.429 185.714-37.143 25.714-100 78.286-146.286 78.286h-1.143c-46.286 0-109.143-52.571-146.286-78.286-88.571-62.286-178.286-124-266.857-185.714-40.571-27.429-98.286-92-98.286-144 0-56 30.286-104 91.429-104h841.143c49.714 0 91.429 41.143 91.429 91.429z"],"width":1024,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["envelope"],"defaultCode":61664,"grid":14},"attrs":[],"properties":{"name":"envelope","id":7,"order":36,"prevSize":28,"code":61664},"setIdx":1,"setId":0,"iconIdx":7},{"icon":{"paths":["M525.143 744.571v-89.714c0-18.857-5.714-28.571-16.571-28.571-6.286 0-12.571 2.857-18.857 9.143v128c6.286 6.286 12.571 9.143 18.857 9.143 10.857 0 16.571-9.143 16.571-28zM630.286 674.857h37.714v-19.429c0-19.429-6.286-29.143-18.857-29.143s-18.857 9.714-18.857 29.143v19.429zM304 522.857v40h-45.714v241.714h-42.286v-241.714h-44.571v-40h132.571zM418.857 594.857v209.714h-38.286v-22.857c-14.857 17.143-29.143 25.714-43.429 25.714-12 0-20.571-5.143-24-16-2.286-6.286-3.429-16-3.429-30.857v-165.714h37.714v154.286c0 8.571 0 13.714 0.571 14.857 0.571 5.714 3.429 8.571 8.571 8.571 8 0 15.429-5.714 24-17.714v-160h38.286zM562.857 658.286v83.429c0 18.857-1.143 33.143-4 41.714-4.571 16-14.857 24-30.286 24-13.143 0-26.286-8-38.857-23.429v20.571h-38.286v-281.714h38.286v92c12-14.857 25.143-22.857 38.857-22.857 15.429 0 25.714 8 30.286 24 2.857 8.571 4 22.286 4 42.286zM706.286 732v5.143c0 12.571-0.571 20.571-1.143 24.571-1.143 8.571-4 16-8.571 22.857-10.286 15.429-26.286 22.857-45.714 22.857-20 0-35.429-7.429-46.286-21.714-8-10.286-12-26.857-12-49.143v-73.714c0-22.286 3.429-38.286 11.429-49.143 10.857-14.286 26.286-21.714 45.714-21.714 18.857 0 34.286 7.429 44.571 21.714 8 10.857 12 26.857 12 49.143v43.429h-76v37.143c0 19.429 6.286 29.143 19.429 29.143 9.143 0 14.857-5.143 17.143-14.857 0-2.286 0.571-10.857 0.571-25.714h38.857zM448.571 261.143v89.143c0 19.429-6.286 29.143-18.286 29.143-12.571 0-18.286-9.714-18.286-29.143v-89.143c0-19.429 5.714-29.714 18.286-29.714 12 0 18.286 10.286 18.286 29.714zM753.143 668.571v0c0-49.143 0-101.143-10.857-148.571-8-33.714-35.429-58.286-68-61.714-77.714-8.571-156.571-8.571-235.429-8.571-78.286 0-157.143 0-234.857 8.571-33.143 3.429-60.571 28-68 61.714-10.857 47.429-11.429 99.429-11.429 148.571v0c0 48.571 0 100.571 11.429 148.571 7.429 33.143 34.857 57.714 67.429 61.714 78.286 8.571 157.143 8.571 235.429 8.571s157.143 0 235.429-8.571c32.571-4 60-28.571 67.429-61.714 11.429-48 11.429-100 11.429-148.571zM321.714 296.571l51.429-169.143h-42.857l-29.143 111.429-30.286-111.429h-44.571c8.571 26.286 18.286 52.571 26.857 78.857 13.714 40 22.286 69.714 26.286 90.286v114.857h42.286v-114.857zM486.857 342.857v-74.286c0-22.286-4-38.857-12-49.714-10.857-14.286-25.714-21.714-44.571-21.714-19.429 0-34.286 7.429-44.571 21.714-8 10.857-12 27.429-12 49.714v74.286c0 22.286 4 38.857 12 49.714 10.286 14.286 25.143 21.714 44.571 21.714 18.857 0 33.714-7.429 44.571-21.714 8-10.286 12-27.429 12-49.714zM590.286 411.429h38.286v-211.429h-38.286v161.714c-8.571 12-16.571 17.714-24 17.714-5.143 0-8.571-2.857-9.143-9.143-0.571-1.143-0.571-5.714-0.571-14.857v-155.429h-38.286v167.429c0 14.857 1.143 24.571 3.429 31.429 4 10.286 12.571 15.429 24.571 15.429 14.286 0 28.571-8.571 44-25.714v22.857zM877.714 237.714v548.571c0 90.857-73.714 164.571-164.571 164.571h-548.571c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571c90.857 0 164.571 73.714 164.571 164.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["youtube-square"],"defaultCode":61798,"grid":14},"attrs":[],"properties":{"name":"youtube-square","id":8,"order":37,"prevSize":28,"code":61798},"setIdx":1,"setId":0,"iconIdx":8},{"icon":{"paths":["M554.857 710.857v120.571c0 25.714-7.429 38.286-22.286 38.286-8.571 0-17.143-4-25.714-12.571v-172c8.571-8.571 17.143-12.571 25.714-12.571 14.857 0 22.286 13.143 22.286 38.286zM748 711.429v26.286h-51.429v-26.286c0-25.714 8.571-38.857 25.714-38.857s25.714 13.143 25.714 38.857zM196 586.857h61.143v-53.714h-178.286v53.714h60v325.143h57.143v-325.143zM360.571 912h50.857v-282.286h-50.857v216c-11.429 16-22.286 24-32.571 24-6.857 0-10.857-4-12-12-0.571-1.714-0.571-8-0.571-20v-208h-50.857v223.429c0 20 1.714 33.143 4.571 41.714 4.571 14.286 16.571 21.143 33.143 21.143 18.286 0 37.714-11.429 58.286-34.857v30.857zM605.714 827.429v-112.571c0-26.286-1.143-45.143-5.143-56.571-6.286-21.143-20.571-32-40.571-32-18.857 0-36.571 10.286-53.143 30.857v-124h-50.857v378.857h50.857v-27.429c17.143 21.143 34.857 31.429 53.143 31.429 20 0 34.286-10.857 40.571-31.429 4-12 5.143-30.857 5.143-57.143zM798.857 821.714v-7.429h-52c0 20.571-0.571 32-1.143 34.857-2.857 13.714-10.286 20.571-22.857 20.571-17.714 0-26.286-13.143-26.286-39.429v-49.714h102.286v-58.857c0-30.286-5.143-52-15.429-66.286-14.857-19.429-34.857-29.143-60.571-29.143-26.286 0-46.286 9.714-61.143 29.143-10.857 14.286-16 36-16 66.286v98.857c0 30.286 5.714 52.571 16.571 66.286 14.857 19.429 34.857 29.143 61.714 29.143s48-10.286 61.714-30.286c6.286-9.143 10.857-19.429 12-30.857 1.143-5.143 1.143-16.571 1.143-33.143zM451.429 300v-120c0-26.286-7.429-39.429-24.571-39.429-16.571 0-24.571 13.143-24.571 39.429v120c0 26.286 8 40 24.571 40 17.143 0 24.571-13.714 24.571-40zM862.286 729.143c0 65.714-0.571 136-14.857 200-10.857 45.143-47.429 78.286-91.429 82.857-105.143 12-211.429 12-317.143 12s-212 0-317.143-12c-44-4.571-81.143-37.714-91.429-82.857-14.857-64-14.857-134.286-14.857-200v0c0-66.286 0.571-136 14.857-200 10.857-45.143 47.429-78.286 92-83.429 104.571-11.429 210.857-11.429 316.571-11.429s212 0 317.143 11.429c44 5.143 81.143 38.286 91.429 83.429 14.857 64 14.857 133.714 14.857 200zM292 0h58.286l-69.143 228v154.857h-57.143v-154.857c-5.143-28-16.571-68-34.857-121.143-12.571-35.429-25.143-71.429-37.143-106.857h60.571l40.571 150.286zM503.429 190.286v100c0 30.286-5.143 53.143-16 67.429-14.286 19.429-34.286 29.143-60.571 29.143-25.714 0-45.714-9.714-60-29.143-10.857-14.857-16-37.143-16-67.429v-100c0-30.286 5.143-52.571 16-66.857 14.286-19.429 34.286-29.143 60-29.143 26.286 0 46.286 9.714 60.571 29.143 10.857 14.286 16 36.571 16 66.857zM694.857 97.714v285.143h-52v-31.429c-20.571 24-40 35.429-58.857 35.429-16.571 0-28.571-6.857-33.714-21.143-2.857-8.571-4.571-22.286-4.571-42.857v-225.143h52v209.714c0 12 0 18.857 0.571 20 1.143 8 5.143 12.571 12 12.571 10.286 0 21.143-8 32.571-24.571v-217.714h52z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["youtube"],"defaultCode":61799,"grid":14},"attrs":[],"properties":{"name":"youtube","id":9,"order":42,"prevSize":28,"code":61799},"setIdx":1,"setId":0,"iconIdx":9},{"icon":{"paths":["M738.286 364.571c4-82.857-26.857-124.571-92-126.857-88-2.857-147.429 46.857-178.286 149.143 16-6.857 31.429-10.857 46.857-10.857 32 0 46.286 18.286 42.286 54.857-1.714 21.714-16 53.714-42.286 95.429-26.857 42.286-46.857 62.857-60 62.857-17.143 0-32-32-46.857-96.571-4.571-19.429-13.143-67.429-25.714-145.714-11.429-72-41.714-105.714-91.429-101.143-20.571 2.286-52.571 20.571-93.714 57.143-30.857 26.857-61.143 54.857-92.571 82.286l29.714 38.286c28.571-19.429 45.143-29.714 49.714-29.714 21.714 0 42.286 34.286 61.143 102.286 17.143 62.857 34.286 125.143 51.429 188 25.714 68 56.571 102.286 93.714 102.286 59.429 0 132.571-56 218.857-168 83.429-107.429 126.857-192 129.143-253.714zM877.714 237.714v548.571c0 90.857-73.714 164.571-164.571 164.571h-548.571c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571c90.857 0 164.571 73.714 164.571 164.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["vimeo-square"],"defaultCode":61844,"grid":14},"attrs":[],"properties":{"name":"vimeo-square","id":10,"order":38,"prevSize":28,"code":61844},"setIdx":1,"setId":0,"iconIdx":10},{"icon":{"paths":["M332.571 747.429c0 30.857-28 37.714-53.143 37.714-24.571 0-61.143-4-61.143-36 0-31.429 30.857-36.571 56-36.571 24 0 58.286 4 58.286 34.857zM312 481.143c0 28.571-11.429 48.571-42.286 48.571-31.429 0-44-18.286-44-48s11.429-51.429 44-51.429c29.143 0 42.286 24 42.286 50.857zM406.857 438.286v-71.429c-24.571 9.143-50.857 16.571-77.143 16.571-18.857-10.857-40.571-16.571-62.857-16.571-65.143 0-116.571 48-116.571 114.286 0 35.429 23.429 84.571 58.857 96.571v1.714c-18.286 8-21.714 30.286-21.714 48.571 0 18.857 6.857 34.286 23.429 44v1.714c-38.857 12.571-64.571 37.143-64.571 79.429 0 72.571 69.143 93.143 129.714 93.143 73.143 0 128-26.857 128-107.429 0-57.143-52-74.286-99.429-82.857-16-2.857-43.429-14.286-43.429-34.286 0-18.857 10.286-26.857 28-29.714 58.286-11.429 95.429-56.571 95.429-116.571 0-10.286-2.286-20-5.714-29.714 9.143-2.286 18.857-4.571 28-7.429zM440.571 677.714h78.286c-1.143-15.429-1.143-31.429-1.143-46.857v-221.143c0-13.143 0-26.286 1.143-39.429h-78.286c1.714 13.143 1.714 27.429 1.714 40.571v224c0 14.286 0 28.571-1.714 42.857zM731.429 668.571v-69.143c-11.429 8-25.143 12-38.857 12-25.714 0-30.286-25.714-30.286-46.857v-128.571h29.714c10.286 0 20 1.143 30.286 1.143v-66.857h-60c0-19.429-1.143-38.857 1.714-58.286h-80c1.714 10.286 2.286 20.571 2.286 31.429v26.857h-34.286v66.857c6.857-0.571 13.714-1.714 21.143-1.714 4 0 8.571 0.571 13.143 0.571v1.143h-1.143v124c0 61.714 9.143 121.143 84.571 121.143 21.143 0 42.857-3.429 61.714-13.714zM528 265.143c0-26.857-20-52-48-52s-48.571 24.571-48.571 52c0 26.857 21.143 50.857 48.571 50.857s48-24.571 48-50.857zM877.714 237.714v548.571c0 90.857-73.714 164.571-164.571 164.571h-548.571c-90.857 0-164.571-73.714-164.571-164.571v-548.571c0-90.857 73.714-164.571 164.571-164.571h548.571c90.857 0 164.571 73.714 164.571 164.571z"],"width":877.7142857142857,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["git-square"],"defaultCode":61906,"grid":14},"attrs":[],"properties":{"name":"git-square","id":11,"order":39,"prevSize":28,"code":61906},"setIdx":1,"setId":0,"iconIdx":11},{"icon":{"paths":["M340 865.143c0-50.286-55.429-57.143-94.286-57.143-40.571 0-90.286 8.571-90.286 59.429 0 51.429 58.857 57.714 98.286 57.714 41.714 0 86.286-10.286 86.286-60zM306.286 433.714c0-42.857-20.571-81.714-68-81.714-52.571 0-70.857 34.857-70.857 82.857 0 47.429 20.571 77.143 70.857 77.143 49.714 0 68-32 68-78.286zM460 248.571v115.429c-14.857 5.143-29.714 9.143-45.143 12.571 5.714 15.429 9.143 31.429 9.143 48 0 96.571-59.429 170.286-154.286 188-28.571 5.714-45.143 17.714-45.143 48.571 0 87.429 230.857 28 230.857 189.143 0 130.857-88.571 173.714-207.429 173.714-97.714 0-209.143-32.571-209.143-150.286 0-68.571 41.714-108 104-128.571v-2.286c-26.286-16-38.286-41.143-38.286-72 0-29.143 6.286-65.143 36-78.286v-2.286c-57.714-19.429-95.429-98.857-95.429-156.571 0-106.857 82.857-185.143 188.571-185.143 35.429 0 70.857 9.143 101.714 26.857 42.857 0 85.143-11.429 124.571-26.857zM641.714 752h-126.857c2.286-25.714 2.286-50.857 2.286-76.571v-348c0-24.571 0.571-49.143-2.286-73.143h126.857c-2.857 23.429-2.286 47.429-2.286 70.857v350.286c0 25.714 0 50.857 2.286 76.571zM985.143 625.143v112c-30.286 16.571-65.143 22.286-99.429 22.286-122.286 0-136.571-96.571-136.571-196v-200.571h1.143v-2.286c-7.429 0-14.286-1.143-21.143-1.143-11.429 0-22.857 1.714-33.714 3.429v-108.571h54.857v-43.429c0-17.143-0.571-34.286-3.429-50.857h129.714c-4.571 31.429-3.429 62.857-3.429 94.286h97.714v108.571c-16.571 0-33.143-2.286-49.143-2.286h-48.571v208.571c0 33.714 7.429 74.857 49.714 74.857 22.286 0 44-6.286 62.286-18.857zM656 84c0 42.857-33.143 82.857-77.143 82.857-45.143 0-78.857-39.429-78.857-82.857 0-44 33.143-84 78.857-84 45.143 0 77.143 41.143 77.143 84z"],"width":1024,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["git"],"defaultCode":61907,"grid":14},"attrs":[],"properties":{"name":"git","id":12,"order":40,"prevSize":28,"code":61907},"setIdx":1,"setId":0,"iconIdx":12},{"icon":{"paths":["M976.571 296c-4 90.286-67.429 214.286-189.714 372-126.857 164-233.143 246.286-321.143 246.286-54.286 0-100-50.286-137.143-150.286-25.143-91.429-50.286-183.429-75.429-275.429-27.429-100-57.714-149.714-89.714-149.714-6.857 0-30.857 14.286-72.571 43.429l-44-56c45.714-40.571 90.857-81.714 136-121.143 60.571-53.714 106.857-80.571 137.714-83.429 72.571-6.857 116.571 42.286 133.714 148 17.714 114.286 30.857 185.714 37.714 213.143 21.143 94.857 43.429 142.286 68.571 142.286 19.429 0 48.571-30.286 88-92 38.857-61.714 59.429-108.571 62.286-140.571 5.143-53.143-15.429-79.429-62.286-79.429-22.286 0-45.143 5.143-69.143 14.857 45.714-149.714 133.143-222.286 262.286-218.286 95.429 2.857 140.571 65.143 134.857 186.286z"],"width":1029.12,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["vimeo"],"defaultCode":62077,"grid":14},"attrs":[],"properties":{"name":"vimeo","id":13,"order":46,"prevSize":28,"code":62077},"setIdx":1,"setId":0,"iconIdx":13},{"icon":{"paths":["M424.633 357.564l-386.090 308.871h946.914v-100h-661.836l163.48-130.785z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":14,"tags":["back"]},"attrs":[{}],"properties":{"order":49,"id":14,"name":"back","prevSize":28,"code":59648},"setIdx":1,"setId":0,"iconIdx":14}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16}} \ No newline at end of file diff --git a/src/assets/images/angle-down.svg b/src/assets/images/angle-down.svg new file mode 100644 index 0000000..1008067 --- /dev/null +++ b/src/assets/images/angle-down.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/favicon.png b/src/assets/images/favicon.png new file mode 100644 index 0000000..0d75435 Binary files /dev/null and b/src/assets/images/favicon.png differ diff --git a/src/assets/images/loader.svg b/src/assets/images/loader.svg new file mode 100644 index 0000000..a656b7e --- /dev/null +++ b/src/assets/images/loader.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/loader.webp b/src/assets/images/loader.webp new file mode 100644 index 0000000..528d6b5 Binary files /dev/null and b/src/assets/images/loader.webp differ diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg new file mode 100644 index 0000000..c1073ec --- /dev/null +++ b/src/assets/images/logo.svg @@ -0,0 +1,109 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/scss/fonts.scss b/src/assets/scss/fonts.scss new file mode 100644 index 0000000..aba175e --- /dev/null +++ b/src/assets/scss/fonts.scss @@ -0,0 +1,10 @@ +@import url('https://fonts.googleapis.com/css2?family=Abel&display=swap'); + +.font-primary { font-family: $font-primary; } +.font-secondary { font-family: $font-secondary; } +.font-bold { font-weight: bold !important; } +.font-light { font-weight: ight !important; } + +@each $size in 8, 10, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 46, 48, 50, 52, 54, 60, 72 { + .font-#{$size} {font-size: #{$size/16}rem !important;} +} diff --git a/src/assets/scss/forms.scss b/src/assets/scss/forms.scss new file mode 100644 index 0000000..60e4caa --- /dev/null +++ b/src/assets/scss/forms.scss @@ -0,0 +1,90 @@ + + +input, +select, +button, +textarea { + border: none; + border-radius: 4px; + background: $white; + appearance: none; + font-family: $font-primary; + font-size: $font-20; + resize: none; + &::-ms-clear { + display: none; + } + &:focus {outline:none;} + &::-moz-focus-inner {border:0;} +} + +input[type=text], +input[type=password], +input[type=file], +select { + color: $black; + padding: 10px 20px; + width: 100%; + text-align: left; + box-sizing: border-box; + &:focus::placeholder { + color: transparent; + } +} + +.input-text { + padding: 10px 20px !important; +} + +.input-file { + padding: 6px 20px !important; +} + +.input-textarea, +.angular-editor { + border-radius: 4px; + background: $white; + padding: 10px; + width: 100%; +} + +.input-select { + padding: 9px 20px !important; + background-image: url('/assets/images/angle-down.svg'); + background-size: 28px; + background-position: right 10px top 10px; + background-repeat: no-repeat; +} + +.button { + position: relative; + appearance: none; + color: $white; + border: none; + border-radius: 4px; + background: $black; + display: inline-block; + padding: 10px 20px 10px 20px !important; + text-align: center; + font-family: $font-20; + text-transform: uppercase; + font-weight: 500; + transition: opacity .3s; + white-space: nowrap; + outline: none; + cursor: pointer; + + &:disabled { + opacity: 0.5; + } + + &.button-white { + background: $white; + color: $black !important; + } + + &.button-transparent { + background: $white-alpha2; + color: $black !important; + } +} diff --git a/src/assets/scss/global.scss b/src/assets/scss/global.scss index 5b51c54..b39e98f 100644 --- a/src/assets/scss/global.scss +++ b/src/assets/scss/global.scss @@ -1,6 +1,43 @@ - body { padding: 0; margin: 0; + height: 100%; + font-family: $font-primary; + font-size: $font-20; + color: $black; + background: $yellow; + overflow-x: hidden; + + &.no-scroll { + overflow: hidden; + } +} + +a, +li, +button { + outline: none !important; + &:active, + &:focus { + outline: none !important; + } +} + +@each $size in 10,20,30,40,50,60,70,80,90,100 { + .w-#{$size} {width: percentage($size/100) !important;} +} + +.text-right {text-align: right !important;} +.text-center {text-align: center !important;} +.text-left {text-align: left !important;} + +.particles { + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + background: radial-gradient(circle, transparent 0%, transparent 85%, rgba(255,255,255,0.2) 95%, rgba(255,255,255,0.3) 100%); + z-index: -1; } diff --git a/src/assets/scss/icons.scss b/src/assets/scss/icons.scss new file mode 100644 index 0000000..67ff190 --- /dev/null +++ b/src/assets/scss/icons.scss @@ -0,0 +1,114 @@ +@font-face { + font-family: 'icomoon'; + src: url('../fonts/icomoon.eot?9ti7zb'); + src: url('../fonts/icomoon.eot?9ti7zb#iefix') format('embedded-opentype'), + url('../fonts/icomoon.ttf?9ti7zb') format('truetype'), + url('../fonts/icomoon.woff?9ti7zb') format('woff'), + url('../fonts/icomoon.svg?9ti7zb#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-alert-circle:before { + content: "\e904"; +} +.icon-alert-triangle:before { + content: "\e905"; +} +.icon-arrow-left:before { + content: "\e901"; +} +.icon-check:before { + content: "\e906"; +} +.icon-chevron-down:before { + content: "\e902"; +} +.icon-corner-right-down:before { + content: "\e907"; +} +.icon-delete:before { + content: "\e903"; +} +.icon-edit:before { + content: "\e908"; +} +.icon-minus-square:before { + content: "\e909"; +} +.icon-plus-square:before { + content: "\e90a"; +} +.icon-share:before { + content: "\e90b"; +} +.icon-trash-2:before { + content: "\e90c"; +} +.icon-x-square:before { + content: "\e90d"; +} +.icon-envelope-o:before { + content: "\f003"; +} +.icon-twitter-square:before { + content: "\f081"; +} +.icon-facebook-square:before { + content: "\f082"; +} +.icon-phone:before { + content: "\f095"; +} +.icon-phone-square:before { + content: "\f098"; +} +.icon-twitter:before { + content: "\f099"; +} +.icon-facebook:before { + content: "\f09a"; +} +.icon-facebook-f:before { + content: "\f09a"; +} +.icon-envelope:before { + content: "\f0e0"; +} +.icon-youtube-square:before { + content: "\f166"; +} +.icon-youtube:before { + content: "\f167"; +} +.icon-vimeo-square:before { + content: "\f194"; +} +.icon-git-square:before { + content: "\f1d2"; +} +.icon-git:before { + content: "\f1d3"; +} +.icon-vimeo:before { + content: "\f27d"; +} +.icon-back:before { + content: "\e900"; +} diff --git a/src/assets/scss/main.scss b/src/assets/scss/main.scss index 369a37a..05094d3 100644 --- a/src/assets/scss/main.scss +++ b/src/assets/scss/main.scss @@ -1,2 +1,8 @@ @import "./variables"; +@import "./fonts"; +@import "./icons"; +@import "./forms"; @import "./global"; + + +@import "node_modules/bootstrap/scss/bootstrap-grid"; diff --git a/src/assets/scss/variables.scss b/src/assets/scss/variables.scss index 7479928..743424d 100644 --- a/src/assets/scss/variables.scss +++ b/src/assets/scss/variables.scss @@ -3,15 +3,15 @@ $grid-breakpoints: ( xs: 0, sm: 576px, md: 768px, - lg: 992px, - xl: 1200px + lg: 1024px, + xl: 1440px ); $container-max-widths: ( sm: 540px, md: 720px, - lg: 960px, - xl: 1140px + lg: 990px, + xl: 1418px ); $grid-columns: 12; @@ -23,23 +23,23 @@ $breadcrumb-height: 60px; // Colors $white: #fff; $black: #000; -$gray: #e8e8e8; -$gray2: #666; -$light-gray: #f5f5f5; -$light-gray2: #c3c3c3; -$dark-gray: #47464e; -$dark-gray2: #2c2c2c; -$gold: #f1c060; -$red: #ea2d31; -$green: #55aa2a; -$blue: #3785ff; +$gray: #999; +$light-gray: #eee; +$dark-gray: #333; +$red: #d00; +$green: #0c0; -$white-alpha: rgba(255, 255, 255, 0.9); -$white-alpha-light: rgba(255, 255, 255, 0.2); +$yellow: #a2dc02; + +$white-alpha: rgba(255, 255, 255, 0.8); +$white-alpha2: rgba(255, 255, 255, 0.4); +$black-alpha: rgba(0, 0, 0, 0.8); +$black-alpha2: rgba(0, 0, 0, 0.4); +$yellow-alpha: rgba(160, 220, 0, 0.8); // Fonts -$font-primary: 'Akzidenz'; -$font-secondary: 'Akzidenz'; +$font-primary: 'Abel'; +$font-secondary: 'Abel'; $font-icon: 'icomoon'; // Font-size variables diff --git a/src/config/config.ts b/src/config/config.ts new file mode 100644 index 0000000..4210776 --- /dev/null +++ b/src/config/config.ts @@ -0,0 +1,52 @@ +import { environment } from '../environments/environment' +import { AngularEditorConfig } from '@kolkov/angular-editor' + +export const editorConfig: AngularEditorConfig = { + editable: true, + spellcheck: true, + height: '300px', + translate: 'no', + enableToolbar: true, + showToolbar: true, + placeholder: 'Enter text here...', + defaultParagraphSeparator: 'p', + defaultFontName: '', + defaultFontSize: '', + uploadUrl: `${environment.API_URL}upload.php`, + uploadWithCredentials: false, + sanitize: true, + toolbarPosition: 'top', + toolbarHiddenButtons: [ + [ + 'undo', + 'redo', + //'bold', + //'italic', + //'underline', + 'strikeThrough', + 'subscript', + 'superscript', + //'justifyLeft', + //'justifyCenter', + //'justifyRight', + //'justifyFull', + 'indent', + 'outdent', + 'insertUnorderedList', + 'insertOrderedList', + 'heading', + 'fontName', + 'fontSize', + 'textColor', + 'backgroundColor', + 'customClasses', + //'link', + 'unlink', + //'insertImage', + 'insertVideo', + 'insertHorizontalRule', + //'removeFormat', + //'toggleEditorMode' + ] + ] +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3612073..a63fed1 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,5 @@ export const environment = { - production: true -}; + production: true, + API_URL: `https://www.dslak.it/apis/`, + BASE_PATH: `https://www.dslak.it` +} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 7b4f817..32977b6 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,16 +1,5 @@ -// This file can be replaced during build by using the `fileReplacements` array. -// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. -// The list of file replacements can be found in `angular.json`. - export const environment = { - production: false -}; - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. + production: false, + API_URL: `http://dslakng.local/apis/`, + BASE_PATH: `http://dslakng.local` +} diff --git a/src/favicon.ico b/src/favicon.ico deleted file mode 100644 index 997406a..0000000 Binary files a/src/favicon.ico and /dev/null differ diff --git a/src/favicon.png b/src/favicon.png new file mode 100644 index 0000000..0d75435 Binary files /dev/null and b/src/favicon.png differ diff --git a/src/index.html b/src/index.html index ccc6ae8..229698c 100644 --- a/src/index.html +++ b/src/index.html @@ -4,8 +4,8 @@ DslakWebsite - - + + diff --git a/src/main.ts b/src/main.ts index c7b673c..5f26277 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,8 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; +import 'hammerjs'; + if (environment.production) { enableProdMode(); }