@ -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----- |
@ -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/ |
|||
|
@ -0,0 +1,41 @@ |
|||
<?php |
|||
@include 'conn.conn'; |
|||
$GLOBALS['conn']; |
|||
$conn = @mysqli_connect($DATAhst,$DATAusr,$DATApwd,$DATAdtb)or die("CONNECTION ERROR"); |
|||
|
|||
$content = null; |
|||
$content->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); |
|||
|
|||
?> |
@ -0,0 +1,16 @@ |
|||
<?php |
|||
|
|||
$DATAhst="localhost"; |
|||
$DATAusr="root"; |
|||
$DATApwd="root"; |
|||
$DATAdtb="www_dslak_it"; |
|||
|
|||
/* |
|||
$DATAhst="localhost"; |
|||
$DATAusr="token"; |
|||
$DATApwd="tokendbpwd"; |
|||
$DATAdtb="www_dslak_it"; |
|||
*/ |
|||
|
|||
|
|||
?> |
@ -0,0 +1,80 @@ |
|||
<?php |
|||
@include 'conn.conn'; |
|||
$GLOBALS['conn']; |
|||
$conn = @mysqli_connect($DATAhst,$DATAusr,$DATApwd,$DATAdtb)or die("CONNECTION ERROR"); |
|||
|
|||
$content = null; |
|||
$data = json_decode(file_get_contents("php://input")); |
|||
|
|||
if(isset($data->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); |
|||
|
|||
?> |
@ -0,0 +1,99 @@ |
|||
<?php |
|||
@include 'conn.conn'; |
|||
$GLOBALS['conn']; |
|||
$conn = @mysqli_connect($DATAhst,$DATAusr,$DATApwd,$DATAdtb)or die("CONNECTION ERROR"); |
|||
|
|||
$content = null; |
|||
|
|||
if(isset($_GET['query'])) { |
|||
|
|||
$content->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); |
|||
|
|||
?> |
@ -0,0 +1,24 @@ |
|||
<?php |
|||
|
|||
$content = null; |
|||
$data = json_decode(file_get_contents("php://input")); |
|||
|
|||
if(isset($data->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); |
|||
|
|||
?> |
@ -0,0 +1,35 @@ |
|||
<?php |
|||
|
|||
$content = null; |
|||
|
|||
if(isset($_POST['token']) && $_POST['token'] == base64_encode('admin:JohnHolmes'.date("Y-m-d"))) { |
|||
|
|||
if(is_uploaded_file($_FILES['file']['tmp_name'])) { |
|||
$file = $_FILES['file']['tmp_name']; |
|||
$filename = date("YmdHis").".".end((explode(".", $_FILES["file"]["name"]))); |
|||
|
|||
$path = isset($_POST['path']) ? "/uploads/".$_POST['path'] : "/uploads/"; |
|||
@move_uploaded_file($file, "..".$path."/".$filename); |
|||
|
|||
http_response_code(200); |
|||
$content->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); |
|||
|
|||
?> |
@ -0,0 +1,75 @@ |
|||
<?php |
|||
@include 'conn.conn'; |
|||
$GLOBALS['conn']; |
|||
$conn = @mysqli_connect($DATAhst,$DATAusr,$DATApwd,$DATAdtb)or die("CONNECTION ERROR"); |
|||
|
|||
$content = null; |
|||
$data = json_decode(file_get_contents("php://input")); |
|||
|
|||
if(isset($data->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); |
|||
|
|||
?> |
@ -0,0 +1,42 @@ |
|||
<div class="component-about container"> |
|||
<div class="row"> |
|||
<div class="col-10 content mx-auto"> |
|||
<button class="back icon-back" (click)="back()"></button> |
|||
|
|||
<p> <b>DSLAK</b> è 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. </p> |
|||
|
|||
<p> 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. </p> |
|||
|
|||
<p> Nel 2011 realizza le sue prime installazioni interattive e ad oggi è alla continua ricerca di soluzioni creative. </p> |
|||
|
|||
<p> <b>DSLAK</b> 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. </p> |
|||
|
|||
|
|||
<div class="about-links pt-4 pb-5"> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/dslakMediaArts"> |
|||
<span class="icon icon-facebook"></span> |
|||
<div class="label">dslakMediaArts</div> |
|||
</a> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/c/Dslak"> |
|||
<span class="icon icon-youtube"></span> |
|||
<div class="label">dslak</div> |
|||
</a> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="https://vimeo.com/dslak"> |
|||
<span class="icon icon-vimeo"></span> |
|||
<div class="label">dslak</div> |
|||
</a> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="https://twitter.com/dslak_"> |
|||
<span class="icon icon-twitter"></span> |
|||
<div class="label">dslak_</div> |
|||
</a> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="tel:+393391805849"> |
|||
<span class="icon icon-phone"></span> |
|||
<div class="label">+39 339 1805849</div> |
|||
</a> |
|||
<a class="link" target="_blank" rel="noopener noreferrer" href="mailto:dslaknma@gmail.com"> |
|||
<span class="icon icon-envelope"></span> |
|||
<div class="label">dslaknma@gmail.com</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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<AboutComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ AboutComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(AboutComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -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() |
|||
} |
|||
} |
@ -0,0 +1,191 @@ |
|||
<div class="component-admin"> |
|||
<div class="row no-gutters" *ngIf="!authCheck"> |
|||
<div class="col-12 col-md-6 mx-auto"> |
|||
<form class="login-form-container" (submit)="login()"> |
|||
<div class="m-2"> |
|||
<span class="login-label">Username</span> |
|||
<input type="text" class="input-text" name="userName" [(ngModel)]="userName"> |
|||
</div> |
|||
<div class="m-2"> |
|||
<span class="login-label">Password</span> |
|||
<input type="password" class="input-text" name="password" [(ngModel)]="password"> |
|||
</div> |
|||
<div class="m-2 pt-4"> |
|||
<button type="submit" class="button">Sign-in</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row no-gutters" *ngIf="authCheck"> |
|||
<div class="col-12 col-md-3 col-lg-2"> |
|||
<div class="menu"> |
|||
<span class="section-title">Works</span> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'works-add'}" (click)="showEditor('works-add')">Add</button> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'works-modify'}" (click)="showEditor('works-modify')">Modify</button> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'works-delete'}" (click)="showEditor('works-delete')">Delete</button> |
|||
|
|||
<span class="section-title">Exhibitions</span> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'exhibitions-add'}" (click)="showEditor('exhibitions-add')">Add</button> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'exhibitions-modify'}" (click)="showEditor('exhibitions-modify')">Modify</button> |
|||
<button class="action" [ngClass]="{'active': activeEditor == 'exhibitions-delete'}" (click)="showEditor('exhibitions-delete')">Delete</button> |
|||
</div> |
|||
</div> |
|||
<div class="col px-5 py-4"> |
|||
<div class="edit-container"> |
|||
<span class="title">{{sectionTitle}}</span> |
|||
<form class="form row" *ngIf="activeEditor == 'works-modify' || activeEditor == 'works-delete'"> |
|||
<div class="col-12"> |
|||
<select class="input-select" (change)="selectWork($event.target.value)"> |
|||
<option value="">- Select work from list -</option> |
|||
<option value="{{work.id}}" *ngFor="let work of works"> |
|||
{{work.type}} | {{work.title}} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
</form> |
|||
<form class="form row" *ngIf="activeEditor == 'exhibitions-modify' || activeEditor == 'exhibitions-delete'"> |
|||
<div class="col-12"> |
|||
<select class="input-select" (change)="selectExhibition($event.target.value)"> |
|||
<option value="">- Select exhibition from list -</option> |
|||
<option value="{{exhibition.id}}" *ngFor="let exhibition of exhibitions"> |
|||
{{exhibition.date_from | date}} | {{exhibition.title}} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
</form> |
|||
<form class="form row" (submit)="saveData()" |
|||
*ngIf="activeEditor == 'works-add' || (activeEditor == 'works-modify' && activeModify) || |
|||
activeEditor == 'exhibitions-add' || (activeEditor == 'exhibitions-modify' && activeModify)"> |
|||
<div [ngClass]="{'col-8': activeEditor == 'works-add' || activeEditor == 'works-modify', |
|||
'col-6': activeEditor == 'exhibitions-add' || activeEditor == 'exhibitions-modify'}"> |
|||
<span class="label">Title</span> |
|||
<input type="text" class="input-text" name="title" [(ngModel)]="title"> |
|||
</div> |
|||
<div class="col-4" *ngIf="activeEditor == 'works-add' || activeEditor == 'works-modify'"> |
|||
<span class="label">Type</span> |
|||
<select class="input-select" name="type" [(ngModel)]="type"> |
|||
<option [value]="sec.section" *ngFor="let sec of workSections">{{sec.title}}</option> |
|||
</select> |
|||
</div> |
|||
<div class="col-3" *ngIf="activeEditor == 'exhibitions-add' || activeEditor == 'exhibitions-modify'"> |
|||
<span class="label">Date from</span> |
|||
<input type="date" class="input-text w-100" name="dateFrom" [(ngModel)]="dateFrom" (change)="dateTo = dateFrom"> |
|||
</div> |
|||
<div class="col-3" *ngIf="activeEditor == 'exhibitions-add' || activeEditor == 'exhibitions-modify'"> |
|||
<span class="label">Date to</span> |
|||
<input type="date" class="input-text w-100" name="dateTo" [(ngModel)]="dateTo"> |
|||
</div> |
|||
<div class="col-12"> |
|||
<span class="label">Content</span> |
|||
<angular-editor [placeholder]="'Enter text here...'" [config]="editorConfig" name="content" [(ngModel)]="content"></angular-editor> |
|||
</div> |
|||
<div class="col-12"> |
|||
<span class="label">Tags</span> |
|||
<input type="text" class="input-text" name="tags" [(ngModel)]="tags"> |
|||
</div> |
|||
<div class="col-12"> |
|||
<span class="label">Gallery</span> |
|||
<div class="gallery-container"> |
|||
|
|||
<label class="image-add" for="image-add"> |
|||
<input type="file" id="image-add" (change)="onFileChanged($event)"> |
|||
</label> |
|||
|
|||
<div class="image-box" [ngClass]="{'main': image.main}" *ngFor="let image of selectedGallery"> |
|||
<img class="image" [src]="basePath+image.url"> |
|||
<button type="button" class="remove" (click)="galleryRemove(image.url)"><span class="icon-trash-2"></span></button> |
|||
<button type="button" class="set-main" (click)="gallerySetMain(image.url)" *ngIf="!image.main"><span class="icon-check"></span></button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-6" *ngIf="activeEditor == 'works-add' || activeEditor == 'works-modify'"> |
|||
<span class="label">Exhibitions</span> |
|||
<select class="input-select" name="exhibitions" (change)="exhibitionAdd($event.target.value)"> |
|||
<option value=""></option> |
|||
<option value="{{exhibition.id}}" *ngFor="let exhibition of exhibitions"> |
|||
{{exhibition.date_from | date}} | {{exhibition.title}} |
|||
</option> |
|||
</select> |
|||
|
|||
<span class="label font-12 pt-2">Selected exhibitions</span> |
|||
<span class="selected-exhibition" *ngFor="let se of selectedExhibitions" (click)="exhibitionRemove(se.id)"> |
|||
{{se.date_from | date}} | {{se.title}} |
|||
</span> |
|||
</div> |
|||
|
|||
<div class="col-6" *ngIf="activeEditor == 'exhibitions-add' || activeEditor == 'exhibitions-modify'"> |
|||
<span class="label">Works</span> |
|||
<select class="input-select" name="works" (change)="workAdd($event.target.value)"> |
|||
<option value=""></option> |
|||
<option value="{{work.id}}" *ngFor="let work of works"> |
|||
{{work.type}} | {{work.title}} |
|||
</option> |
|||
</select> |
|||
|
|||
<span class="label font-12 pt-2">Selected works</span> |
|||
<span class="selected-work" *ngFor="let sw of selectedWorks" (click)="workRemove(sw.id)"> |
|||
{{sw.type}} | {{sw.title}} |
|||
</span> |
|||
|
|||
</div> |
|||
|
|||
<div class="col-6"> |
|||
<span class="label">Video</span> |
|||
<div class="w-30 d-inline-block pr-2"> |
|||
<select class="input-select" name="videoType" [(ngModel)]="videoType"> |
|||
<option value="youtube">YouTube</option> |
|||
<option value="vimeo">Vimeo</option> |
|||
<option value="embed">Embed</option> |
|||
</select> |
|||
</div> |
|||
<div class="w-60 d-inline-block pr-2"> |
|||
<input type="text" class="input-text" name="videoURL" [(ngModel)]="videoURL"> |
|||
</div> |
|||
<div class="w-10 d-inline-block"> |
|||
<span class="button button-transparent icon-plus-square px-0 w-100" (click)="videoAdd()"></span> |
|||
</div> |
|||
|
|||
<span class="label font-12 pt-2">Selected Videos</span> |
|||
<span class="selected-video" *ngFor="let sv of selectedVideos" (click)="videoRemove(sv.url)"> |
|||
{{sv.type}} | {{sv.url}} |
|||
</span> |
|||
</div> |
|||
<div class="col-12 pt-5"> |
|||
<button class="button w-100" type="submit">Save</button> |
|||
</div> |
|||
</form> |
|||
|
|||
|
|||
<form class="form row" (submit)="deleteData(modifyId)" |
|||
*ngIf="(activeEditor == 'works-delete' || activeEditor == 'exhibitions-delete') && modifyId"> |
|||
<div class="col-12"> |
|||
<span class="label">Title</span> |
|||
<div class="preview-box" *ngIf="activeEditor == 'works-delete'">{{type}} | {{title}}</div> |
|||
<div class="preview-box" *ngIf="activeEditor == 'exhibitions-delete'">{{dateFrom}} | {{title}}</div> |
|||
</div> |
|||
<div class="col-12"> |
|||
<span class="label">Content</span> |
|||
<div class="preview-box" [innerHTML]="content"></div> |
|||
</div> |
|||
|
|||
<div class="col-12"> |
|||
<span class="label">Gallery</span> |
|||
<div class="gallery-container"> |
|||
<div class="image-box" [ngClass]="{'main': image.main}" *ngFor="let image of selectedGallery"> |
|||
<img class="image" [src]="basePath+image.url"> |
|||
<button type="button" class="remove" (click)="galleryRemove(image.url)"><span class="icon-trash-2"></span></button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-12 pt-5"> |
|||
<button class="button w-100" type="submit">Delete</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
@ -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%; |
|||
} |
|||
} |
|||
} |
@ -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<AdminComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ AdminComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(AdminComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -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 = (<HTMLInputElement>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 |
|||
} |
|||
} |
@ -1,2 +1,7 @@ |
|||
<app-header></app-header> |
|||
<p>app-layout works!</p> |
|||
<main> |
|||
<app-header *ngIf="page != '/admin'"></app-header> |
|||
<router-outlet></router-outlet> |
|||
|
|||
<Particles class="particles" *ngIf="particlesEnabled && page != '/admin'" |
|||
[id]="id" [options]="particlesOptions" (particlesLoaded)="particlesLoaded($event)"></Particles> |
|||
</main> |
|||
|
@ -0,0 +1,3 @@ |
|||
main { |
|||
overflow: hidden; |
|||
} |
@ -0,0 +1,76 @@ |
|||
<div class="component-detail container"> |
|||
<div class="row"> |
|||
<div class="col-11 col-md-10 col-lg-12 content mx-auto"> |
|||
<button class="back icon-back" (click)="back()"></button> |
|||
<h2 class="title">{{details.title}}</h2> |
|||
|
|||
<div class="date-container" *ngIf="section == 'exhibitions'"> |
|||
<span class="date-indication" *ngIf="details.date_from != details.date_to">from</span> |
|||
<span class="date-indication" *ngIf="details.date_from == details.date_to">on</span> |
|||
<span class="date">{{details.date_from | date}}</span> |
|||
<span class="date" *ngIf="details.date_from != details.date_to"> <span class="date-indication">to</span> {{details.date_to | date}}</span> |
|||
</div> |
|||
|
|||
<div class="row no-gutters gallery" *ngIf="galleryImages.length"> |
|||
<div class="col-12" *ngFor="let image of galleryImages; let i = index" |
|||
[ngClass]="{'col-md-4': galleryImages.length < 4 && (galleryImages.length % 3) == 2, |
|||
'col-md-12': galleryImages.length < 4 && (galleryImages.length % 3) == 1, |
|||
'col-md-3': galleryImages.length >= 3}"> |
|||
<div class="gallery-container"> |
|||
<span class="gallery-title">{{image.title}}</span> |
|||
<img class="image" *ngIf="loadedImages[i]" src="/assets/images/loader.svg" alt="loading"> |
|||
<img class="image" [hidden]="loadedImages[i]" (load)="onLoad(i)" [src]="image.url" (click)="openGallery(i)"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="text" [innerHTML]="details.content"></div> |
|||
|
|||
<div class="row videos" *ngIf="details.videos && details.videos.length"> |
|||
<div class="col-12" *ngFor="let video of details.videos" |
|||
[ngClass]="{'col-md-6': details.videos.length < 4 && (details.videos.length % 3) == 2, |
|||
'col-md-12': details.videos.length < 4 && (details.videos.length % 3) == 1, |
|||
'col-md-4': details.videos.length >= 3}"> |
|||
<span class="video-title">{{video.title}}</span> |
|||
<div class="youtube" *ngIf="video.type == 'youtube'"> |
|||
<iframe class="iframe" [src]="video.embed" |
|||
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" |
|||
allowfullscreen></iframe> |
|||
</div> |
|||
<div class="vimeo" *ngIf="video.type == 'vimeo'"> |
|||
<iframe class="iframe" title="vimeo-player" [src]="video.embed" src="https://player.vimeo.com/video/104753208" |
|||
frameborder="0" allowfullscreen></iframe> |
|||
</div> |
|||
<div class="embed" *ngIf="video.type == 'embed'" [innerHTML]="video.embed"></div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<span class="tags" *ngIf="details.tags"><b>Tags:</b> {{details.tags}}</span> |
|||
|
|||
<span class="links" *ngIf="details.exhibitions && details.exhibitions.length"><b>Exhibitions:</b> |
|||
<span class="link" *ngFor="let exhibition of details.exhibitions" |
|||
(click)="showDetails('exhibitions', exhibition.id, exhibition.title)" |
|||
routerLink="/detail/exhibitions/{{exhibition.id}}">{{exhibition.title}} </span> |
|||
</span> |
|||
|
|||
<span class="links" *ngIf="details.works && details.works.length"><b>Works:</b> |
|||
<span class="link" *ngFor="let work of details.works" |
|||
(click)="showDetails('works', work.id, work.title)" |
|||
routerLink="/detail/works/{{work.id}}">{{work.title}} </span> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<ngx-image-gallery |
|||
[images]="galleryImages" |
|||
[conf]="conf" |
|||
(onOpen)="galleryOpened($event)" |
|||
(onClose)="galleryClosed()" |
|||
(onImageClicked)="galleryImageClicked($event)" |
|||
(onImageChange)="galleryImageChanged($event)" |
|||
(onDelete)="deleteImage($event)" |
|||
></ngx-image-gallery> |
|||
|
|||
<app-spinner [show]="!loaded"></app-spinner> |
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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<DetailComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ DetailComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(DetailComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -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) |
|||
} |
|||
} |
@ -1,3 +1,21 @@ |
|||
<header class="component-header"> |
|||
<header class="component-header" [ngClass]="{'sticky': isSticky}"> |
|||
<div class="logo-container" [ngClass]="{'menu-open': isMenuOpen}"> |
|||
<span class="circles"></span> |
|||
<img class="logo" src="assets/images/logo.svg" (click)="toggleMenu()"> |
|||
</div> |
|||
|
|||
<div class="menu" [ngClass]="{'open': isMenuOpen}"> |
|||
<nav class="nav"> |
|||
<ul class="items"> |
|||
<li class="item" routerLink="/about" [routerLinkActive]="'active'">About</li> |
|||
<li class="item" routerLink="/portfolio" [routerLinkActive]="'active'">Portfolio</li> |
|||
<li class="item" routerLink="/exhibitions" [routerLinkActive]="'active'">Exhibitions</li> |
|||
<li class="item" routerLink="/installations" [routerLinkActive]="'active'">Installations</li> |
|||
<li class="item" routerLink="/entertainment" [routerLinkActive]="'active'">Entertainment</li> |
|||
<li class="item" routerLink="/performances" [routerLinkActive]="'active'">Performances</li> |
|||
<li class="item" routerLink="/workshops" [routerLinkActive]="'active'">Workshops</li> |
|||
</ul> |
|||
</nav> |
|||
</div> |
|||
|
|||
</header> |
|||
|
@ -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); } |
|||
} |
|||
|
@ -0,0 +1,31 @@ |
|||
<div class="component-home"> |
|||
<button class="goto-prev" (click)="scroll('prev')" (mouseover)="paused=true"><span class="icon icon-back"></span></button> |
|||
<button class="goto-next" (click)="scroll('next')" (mouseover)="paused=true"><span class="icon icon-back"></span></button> |
|||
<div class="content" #scrollContent (touchstart)="swipe($event, 'start')" (touchend)="swipe($event, 'end')"> |
|||
<div [ngClass]="'slide-' + item.width" *ngFor="let item of homeItems"> |
|||
<div class="box" [ngClass]="'skew-' + (item.id % 6)" (click)="showDetails(item.id, item.title)" (mouseover)="paused=true" (mouseout)="paused=false"> |
|||
<img class="image" *ngIf="item.loading" src="/assets/images/loader.svg" alt="loading"> |
|||
<img class="image" [hidden]="item.loading" (load)="onLoad(item.id)" [src]="basePath+item.image"> |
|||
<div class="text"> |
|||
<span class="title">{{item.title}}</span> |
|||
<span class="type" *ngIf="section != 'exhibitions'">{{item.type}}</span> |
|||
|
|||
<div class="date-container" *ngIf="section == 'exhibitions'"> |
|||
<div class="date-row"> |
|||
<span class="date-indication" *ngIf="item.date_from != item.date_to">from</span> |
|||
<span class="date-indication" *ngIf="item.date_from == item.date_to">on</span> |
|||
<span class="date">{{item.date_from | date}}</span> |
|||
</div> |
|||
<div class="date-row"> |
|||
<span class="date" *ngIf="item.date_from != item.date_to"> <span class="date-indication">to</span> {{item.date_to | date}}</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<span class="tags">{{item.tags}}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<app-spinner [show]="!loaded"></app-spinner> |
@ -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;} |
|||
} |
@ -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<HomeComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ HomeComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(HomeComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -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' |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
<div class="component-portfolio"> |
|||
<div class="row no-gutters row-container"> |
|||
<div class="col-12 col-sm-6 mx-auto" [ngClass]="'col-md-' + item.width" *ngFor="let item of portfolioItems"> |
|||
<div class="box" (click)="showDetails(item.id, item.title)"> |
|||
<img class="loader" *ngIf="item.loading" src="/assets/images/loader.svg" alt="loading"> |
|||
<img class="image" [hidden]="item.loading" (load)="onLoad(item.id)" [src]="basePath+item.image"> |
|||
<div class="text"> |
|||
<span class="title">{{item.title}}</span> |
|||
<span class="type" *ngIf="section != 'exhibitions'">{{item.type}}</span> |
|||
|
|||
<div class="date-container" *ngIf="section == 'exhibitions'"> |
|||
<div class="date-row"> |
|||
<span class="date-indication" *ngIf="item.date_from != item.date_to">from</span> |
|||
<span class="date-indication" *ngIf="item.date_from == item.date_to">on</span> |
|||
<span class="date">{{item.date_from | date}}</span> |
|||
</div> |
|||
<div class="date-row"> |
|||
<span class="date" *ngIf="item.date_from != item.date_to"> <span class="date-indication">to</span> {{item.date_to | date}}</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<span class="tags">{{item.tags}}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<app-spinner [show]="!loaded"></app-spinner> |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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<PortfolioComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ PortfolioComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(PortfolioComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -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) |
|||
} |
|||
|
|||
} |
@ -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(); |
|||
}); |
|||
}); |
@ -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<any> { |
|||
let urlApi = `${this.restApi}?query=${section}&random=${randon}` |
|||
return this.http.get<any>(urlApi).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
getDetails(section, id): Observable<any> { |
|||
let urlApi = `${this.restApi}?query=detail&type=${section}&id=${id}` |
|||
return this.http.get<any>(urlApi).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
// ADMIN SERVICES
|
|||
uploadImage(body): Observable<any> { |
|||
let urlApi = `${this.restApi}upload.php` |
|||
return this.http.post<any>(urlApi, body).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
removeImage(body): Observable<any> { |
|||
let urlApi = `${this.restApi}remove.php` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
saveWork(body): Observable<any> { |
|||
let urlApi = `${this.restApi}work.php?act=save` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
deleteWork(body): Observable<any> { |
|||
let urlApi = `${this.restApi}work.php?act=delete` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
saveExhibition(body): Observable<any> { |
|||
let urlApi = `${this.restApi}exhibition.php?act=save` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
deleteExhibition(body): Observable<any> { |
|||
let urlApi = `${this.restApi}exhibition.php?act=delete` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
|
|||
} |
@ -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(); |
|||
}); |
|||
}); |
@ -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<any> { |
|||
let urlApi = `${this.restApi}auth.php?act=login` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
authCheck(body): Observable<any> { |
|||
let urlApi = `${this.restApi}auth.php?act=check` |
|||
return this.http.post<any>(urlApi, JSON.stringify(body)).pipe( |
|||
catchError(this.handleError) |
|||
) |
|||
} |
|||
|
|||
} |
@ -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) ) |
|||
} |
|||
} |
@ -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>(.*?)<\/Message>/g)[0].replace(/<[^>]+>/g, '') |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
|
|||
<div class="spinner" #spinner></div> |
@ -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; |
|||
} |
@ -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 |
|||
} |
|||
} |
|||
} |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 432 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 7.4 KiB |
@ -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;} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
@ -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"; |
|||
} |
@ -1,2 +1,8 @@ |
|||
@import "./variables"; |
|||
@import "./fonts"; |
|||
@import "./icons"; |
|||
@import "./forms"; |
|||
@import "./global"; |
|||
|
|||
|
|||
@import "node_modules/bootstrap/scss/bootstrap-grid"; |
|||
|
@ -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'
|
|||
] |
|||
] |
|||
} |
@ -1,3 +1,5 @@ |
|||
export const environment = { |
|||
production: true |
|||
}; |
|||
production: true, |
|||
API_URL: `https://www.dslak.it/apis/`, |
|||
BASE_PATH: `https://www.dslak.it` |
|||
} |
|||
|
@ -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` |
|||
} |
|||
|
Before Width: | Height: | Size: 948 B |
After Width: | Height: | Size: 1.5 KiB |