diff --git a/config/packages/security.yaml b/config/packages/security.yaml index dbb930a..8186fa5 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -30,6 +30,10 @@ security: check_path: users_login logout: path: users_logout + json_login: + check_path: api_users_login + username_path: email + password_path: password # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used diff --git a/public/scripts/chart_loader.js b/public/scripts/chart_loader.js index 0f748c2..be20f88 100644 --- a/public/scripts/chart_loader.js +++ b/public/scripts/chart_loader.js @@ -166,10 +166,14 @@ class ChartLoader { let tooltipPos = obj[0].getTooltipPos() // Adjust tooltip position to fit within the chart div - if (tooltipPos.right.x + this.tooltip.clientWidth <= this.parent.clientWidth - 2) + if (tooltipPos.right.x + this.tooltip.clientWidth <= this.parent.clientWidth - 2) { + this.tooltip.className = "tooltip leftArrow" this.tooltip.style.left = tooltipPos.right.x + 5 + "px" - else + } + else { + this.tooltip.className = "tooltip rightArrow" this.tooltip.style.left = tooltipPos.left.x - this.tooltip.clientWidth + "px" + } // Check if the tooltip extends beyond the bottom edge of the chart div if (tooltipPos.right.y + this.tooltip.clientHeight <= parent.clientHeight - 2) diff --git a/public/scripts/charts/primitives.js b/public/scripts/charts/primitives.js index 21430ac..aa35ad2 100644 --- a/public/scripts/charts/primitives.js +++ b/public/scripts/charts/primitives.js @@ -214,9 +214,14 @@ class PieSlice extends Circle { * @returns {Object} An object containing x and y coordinates for the tooltip position. */ getTooltipPos() { + let angle = (this.sAngle + this.eAngle) / 2 + let pos = { + x: this.x + Math.cos(angle) * this.r, + y: this.y + Math.sin(angle) * this.r + } return { - right: this.getCenter(), - left: this.getCenter() + right: pos, + left: pos } } } diff --git a/public/styles/chart_style.css b/public/styles/chart_style.css index 44291f8..a1bd24b 100644 --- a/public/styles/chart_style.css +++ b/public/styles/chart_style.css @@ -48,8 +48,8 @@ body { /* Styles for legend items */ #chartLegend div { display: inline; - margin: 0 10px 0 10px; /* Spacing between legend items */ - width: 50px; + margin: 0 1rem 0 1rem; /* Spacing between legend items */ + width: 5rem; } /* Styles for the tooltip */ @@ -59,8 +59,9 @@ body { background-color: white; border: 1px solid grey; text-align: center; - padding: 5px; - border-radius: 3px; + padding: 0.5rem; + border-radius: 0.1rem; + /*box-shadow: 0.2rem 0.2rem 0.2rem lightgrey;*/ z-index: 1; /* Ensure tooltip appears above other elements */ } @@ -76,7 +77,7 @@ body { } /* Styles for tooltip arrow */ -#tooltip::after { +.leftArrow::after { content: ""; /* Create a pseudo-element */ position: absolute; /* Position relative to its containing block */ top: 50%; /* Position vertically centered */ @@ -85,4 +86,15 @@ body { border-width: 0.5rem; /* Size of the arrow */ border-style: solid; /* Solid border */ border-color: transparent white transparent transparent; /* Arrow color */ +} + +.rightArrow::before { + content: ""; /* Create a pseudo-element */ + position: absolute; /* Position relative to its containing block */ + top: 50%; /* Position vertically centered */ + left: 100%; /* Position to the left of the tooltip */ + margin-top: -0.5rem; /* Adjust vertical position */ + border-width: 0.5rem; /* Size of the arrow */ + border-style: solid; /* Solid border */ + border-color: transparent transparent transparent white; /* Arrow color */ } \ No newline at end of file diff --git a/public/styles/edit_chart.css b/public/styles/edit_chart.css index c562c75..a2157d3 100755 --- a/public/styles/edit_chart.css +++ b/public/styles/edit_chart.css @@ -25,6 +25,7 @@ main form { text-align: center; overflow: scroll; flex-basis: 25%; + margin-right: 0.5rem; } #settings_div h1{ @@ -172,28 +173,26 @@ div[id^="chart_metadata_group"] { } /* Media query for smaller screens */ -@media (max-width: 950px) { +@media (max-width: 760px) { #mainDiv { - display: block + flex-direction: column-reverse; } #chartDiv { - width: 100%; + width: auto; + flex-basis: 60%; } #settings_div { - width: 100%; - display: block + margin-top: 1rem; + width: auto; + flex-basis: 40%; } #tableDiv { width: 100% } - #shareDiv { - width: 100% - } - #secondaryDiv { display: block } diff --git a/public/styles/web_style.css b/public/styles/web_style.css index 3164043..d9b64c5 100755 --- a/public/styles/web_style.css +++ b/public/styles/web_style.css @@ -17,16 +17,21 @@ body { padding: 0; } -button, #createChart { +button, +#createChart { background-color: var(--main-dark); border: none; color: white; - padding: 10px 15px; + padding: 1rem 1.5rem; text-align: center; text-decoration: none; display: inline-block; - font-size: 16px; - margin: 5px; + margin: 0.3rem; + font-size: 1rem; +} + +#createChart { + font-size: 1.5rem; } button:hover { @@ -52,7 +57,8 @@ header nav { justify-content: flex-end; } -header #homepageLink:hover, header #chartsLink:hover { +header #homepageLink:hover, +header #chartsLink:hover { background-color: var(--main-highlight); } @@ -123,7 +129,8 @@ h2 { flex-direction: column; } -.loginDiv #user div, .loginDiv form > div { +.loginDiv #user div, +.loginDiv form>div { width: auto; display: flex; flex-direction: column; @@ -133,10 +140,27 @@ h2 { color: red; } -.loginDiv #logoutBtn { +.loginDiv #logoutBtn, +#deleteBtn { color: red; } +#accountButtonsDiv { + height: 6rem; + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +#logoutBtn { + margin-bottom: 2rem; +} + +#logoutBtn:hover, +#deleteBtn:hover { + color: darkred; +} + #myCharts { height: 100%; width: 100%; @@ -162,7 +186,7 @@ h2 { flex-direction: column; } -#myChartList span{ +#myChartList span { width: 80%; margin: 10px 10%; /*border: 1px solid var(--main-dark);*/ @@ -170,7 +194,7 @@ h2 { display: flex; } -#myChartList span a{ +#myChartList span a { text-decoration: none; display: inline-block; height: 100%; @@ -187,7 +211,7 @@ h2 { background-color: var(--main-highlight); } -#myChartList #removeChart{ +#myChartList #removeChart { color: red; display: inline-block; text-align: middle; @@ -195,8 +219,8 @@ h2 { transition: 300ms; } -#myChartList #removeChart:hover{ +#myChartList #removeChart:hover { background-color: var(--main-highlight); color: red; transition: 300ms; -} +} \ No newline at end of file diff --git a/src/Api/Controller/ChartRestController.php b/src/Api/Controller/ChartRestController.php index f9e6f9a..2d7ed8f 100644 --- a/src/Api/Controller/ChartRestController.php +++ b/src/Api/Controller/ChartRestController.php @@ -11,30 +11,27 @@ use FOS\RestBundle\Controller\Annotations as Rest; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -#[Rest\Route(path: '/api/charts', name: 'api_charts')] +#[Rest\Route(path: '/api/charts', name: 'api_charts_')] class ChartRestController extends AbstractRestController { - #[Rest\Get('/', name: '_list')] + #[Rest\Get('/', name: 'list')] public function list(DocumentManager $dm, Request $request) : Response { - $charts = \array_map(fn(Chart $entity) => ChartOutput::fromEntity($entity)->id, $dm->getRepository(Chart::class)->findAll()); + $charts = \array_map(fn(Chart $entity) => ChartOutput::fromEntity($entity)->id, iterator_to_array($this->getUser()->getCharts())); return $this->respond($charts, Response::HTTP_OK); } - #[Rest\Get('/{id}', name: '_detail')]//, requirements: ['id' => '\d+'])] + #[Rest\Get('/{id}', name: 'detail')] public function detail(DocumentManager $dm, Chart $chart): Response { - //$chart = $this->findOrFail($dm, $id); - $chartDto = ChartOutput::fromEntity($chart); return $this->respond( $chartDto, Response::HTTP_OK); } - #[Rest\Put('/insert', name: '_insert')] + #[Rest\Post('/insert', name: 'insert')] public function insert(DocumentManager $dm, Request $request): Response { - //$chart = $newChart->toEntity(); $data = json_decode($request->getContent()); $chart = new Chart(); $chart->setName($data->name); @@ -46,15 +43,25 @@ class ChartRestController extends AbstractRestController return $this->respond( $chart->getName(), Response::HTTP_OK); } - /*private function findOrFail(DocumentManager $dm, int $id): Chart + #[Rest\Post('/{id}/update', name: 'update')] + public function update(DocumentManager $dm, Request $request, Chart $chart): Response { - //$chart = $dm->getRepository(Chart::class)->findOneBy(['code' => $id]); - $chart = $dm->getRepository(Chart::class)->findOneBy(['_id' => $id]); + $data = json_decode($request->getContent()); + $chart->setName($data->name); + $chart->setMetadata($data->metadata); + $chart->setTable($data->table); + + $dm->persist($chart); + $dm->flush(); + return $this->respond( $chart->getName(), Response::HTTP_OK); + } - if ($chart === null) { - throw $this->createNotFoundException("Chart was not found"); - } + #[Rest\Delete('/{id}/remove', name:'remove')] + public function remove(DocumentManager $dm, Request $request, Chart $chart): Response + { + $dm->remove($chart); + $dm->flush(); - return $chart ; - }*/ + return $this->respond( $chart->getName(), Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Api/Controller/UserRestController.php b/src/Api/Controller/UserRestController.php new file mode 100644 index 0000000..aed7a26 --- /dev/null +++ b/src/Api/Controller/UserRestController.php @@ -0,0 +1,23 @@ +json([ + 'user' => $user ? $user->getId() : null, + ]); + } +} \ No newline at end of file diff --git a/src/Controller/ChartController.php b/src/Controller/ChartController.php index cab716d..93bb751 100644 --- a/src/Controller/ChartController.php +++ b/src/Controller/ChartController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Document\Chart; use App\Form\Type\ChartType; +use App\Form\Type\DeleteType; use Doctrine\ODM\MongoDB\DocumentManager; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -63,11 +64,22 @@ class ChartController extends AbstractController } #[Route('/{id}/remove', name: 'remove')] - public function removeAction(DocumentManager $dm, Chart $chart) : Response + public function removeAction(DocumentManager $dm, Request $request, Chart $chart) : Response { - $dm->remove($chart); - //$this->getUser()->removeChart($chart); - $dm->flush(); - return $this->redirectToRoute('charts_list'); + $form = $this->createForm(DeleteType::class, $chart); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $dm->remove($chart); + //$this->getUser()->removeChart($chart); + $dm->flush(); + + return $this->redirectToRoute('charts_list'); + } + + return $this->render('chart/remove.html.twig', [ + 'form' =>$form->createView(), + 'entity' => $chart + ]); } } diff --git a/src/Controller/IndexController.php b/src/Controller/IndexController.php index b229695..5206202 100644 --- a/src/Controller/IndexController.php +++ b/src/Controller/IndexController.php @@ -14,4 +14,9 @@ class IndexController extends AbstractController return $this->render('index.html.twig'); } + #[Route('/docs', name: 'docs')] + public function docsAction(): Response + { + return $this->render('docs.html.twig'); + } } \ No newline at end of file diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 341d03c..fb1a94f 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Document\User; use App\Form\Type\UserType; use App\Form\Type\LoginType; +use App\Form\Type\DeleteType; use Doctrine\ODM\MongoDB\DocumentManager; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -30,6 +31,9 @@ class UserController extends AbstractController if ($form->isSubmitted() && $form->isValid()) { $user = $form->getData(); + if ($dm->getRepository(User::class)->findOneBy(['email'=> $user->getEmail()])) + throw $this->createAccessDeniedException('Email již někdo používá'); + $hashedPassword = $passwordHasher->hashPassword($user, $user->getPassword()); $user->setPassword($hashedPassword); @@ -83,4 +87,24 @@ class UserController extends AbstractController 'form' => $form->createView() ]); } + + #[Route('/{id}/remove', name: 'remove')] + public function removeAction(DocumentManager $dm, Request $request, User $user) : Response + { + $form = $this->createForm(DeleteType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $user->getCharts()->clear(); + $dm->remove($user); + $dm->flush(); + + return $this->redirectToRoute('userss_list'); + } + + return $this->render('user/remove.html.twig', [ + 'form' =>$form->createView(), + 'entity' => $user + ]); + } } diff --git a/src/Form/Type/DeleteType.php b/src/Form/Type/DeleteType.php new file mode 100644 index 0000000..e2b6be8 --- /dev/null +++ b/src/Form/Type/DeleteType.php @@ -0,0 +1,29 @@ +configureOptions($resolver); + + $builder + ->add('button', SubmitType::class, [ + 'label'=> 'Smazat', + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + /*$resolver + ->setRequired('entity') + ->setAllowedTypes('entity', 'string');*/ + } +} \ No newline at end of file diff --git a/templates/bundles/TwigBundle/Exception/error.html.twig b/templates/bundles/TwigBundle/Exception/error.html.twig index f73efff..be5ee53 100644 --- a/templates/bundles/TwigBundle/Exception/error.html.twig +++ b/templates/bundles/TwigBundle/Exception/error.html.twig @@ -117,6 +117,7 @@ +
Přihlásit
diff --git a/templates/chart/remove.html.twig b/templates/chart/remove.html.twig new file mode 100644 index 0000000..e5e4ef0 --- /dev/null +++ b/templates/chart/remove.html.twig @@ -0,0 +1,16 @@ +{% extends 'base.html.twig' %} + +{% block title %} + Smazat graf +{% endblock %} + +{% block body %} + {{ parent() }} +
+
+

Smazání grafu

+

Opravdu chcete smazat graf: {{ entity.name }} ?

+ {{ form(form) }} +
+
+{% endblock %} diff --git a/templates/docs.html.twig b/templates/docs.html.twig new file mode 100644 index 0000000..0e3d6a8 --- /dev/null +++ b/templates/docs.html.twig @@ -0,0 +1,16 @@ +{% extends 'base.html.twig' %} + +{% block title %} + Dokumentace +{% endblock %} + +{% block body %} + {{ parent() }} +

Interaktivní grafy

+
+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus odio neque est voluptatum + repellat animi dicta aliquam ipsa culpa commodi. +

+
+{% endblock %} diff --git a/templates/header.html.twig b/templates/header.html.twig index 0e38e7a..680dc83 100644 --- a/templates/header.html.twig +++ b/templates/header.html.twig @@ -3,7 +3,10 @@ Index
{% if app.user %} diff --git a/templates/index.html.twig b/templates/index.html.twig index 7a36d6a..bf7bd36 100644 --- a/templates/index.html.twig +++ b/templates/index.html.twig @@ -1,7 +1,7 @@ {% extends 'base.html.twig' %} {% block title %} - Home + Domovská stránka {% endblock %} {% block body %} diff --git a/templates/user/edit.html.twig b/templates/user/edit.html.twig index 81d3845..03fa052 100644 --- a/templates/user/edit.html.twig +++ b/templates/user/edit.html.twig @@ -14,7 +14,10 @@ {{ form_end(form, {'render_rest': false}) }} - Odhlásit se +
+ Odhlásit se + Smazat uživatelský účet +
{% endblock %} diff --git a/templates/user/remove.html.twig b/templates/user/remove.html.twig new file mode 100644 index 0000000..45213be --- /dev/null +++ b/templates/user/remove.html.twig @@ -0,0 +1,16 @@ +{% extends 'base.html.twig' %} + +{% block title %} + Smazat uživatele +{% endblock %} + +{% block body %} + {{ parent() }} +
+
+

Smazání uživatelského účtu

+

Opravdu chcete smazat účet: {{ entity.email }} ?

+ {{ form(form) }} +
+
+{% endblock %}