From 3e89c654ec7df057002ef04b92eb22f1d781cd76 Mon Sep 17 00:00:00 2001 From: Kacper Donat <kadet1090@gmail.com> Date: Sun, 9 Sep 2018 13:05:01 +0200 Subject: [PATCH] multiple provider support --- config/routes.yaml | 7 ++-- config/services.yaml | 14 ++++++-- package.json | 7 ++-- resources/components/departures.html | 4 +++ resources/components/finder.html | 2 ++ resources/styles/_common.scss | 16 +++++++++ resources/styles/main.scss | 2 +- resources/ts/components/departures.ts | 1 - resources/ts/components/picker.ts | 24 ++----------- resources/ts/urls.ts | 12 ++++--- .../{ => Api/v1}/DeparturesController.php | 5 +-- .../{ => Api/v1}/MessagesController.php | 5 +-- .../{ => Api/v1}/StopsController.php | 5 +-- src/Controller/HomepageController.php | 17 --------- src/Controller/MainController.php | 27 ++++++++++++++ src/Provider/Provider.php | 3 ++ .../ZtmGdanskDataUpdateSubscriber.php | 22 +++++++----- src/Provider/ZtmGdanskProvider.php | 12 ++++++- src/Service/EntityConverter.php | 2 ++ src/Service/ProviderResolver.php | 35 ++++++++----------- templates/app.html.twig | 13 +++++++ templates/base.html.twig | 17 ++++----- templates/choose.html.twig | 11 ++++++ webpack.config.js | 9 ++++- yarn.lock | 8 +++++ 25 files changed, 183 insertions(+), 97 deletions(-) rename src/Controller/{ => Api/v1}/DeparturesController.php (91%) rename src/Controller/{ => Api/v1}/MessagesController.php (78%) rename src/Controller/{ => Api/v1}/StopsController.php (92%) delete mode 100644 src/Controller/HomepageController.php create mode 100644 src/Controller/MainController.php create mode 100644 templates/app.html.twig create mode 100644 templates/choose.html.twig diff --git a/config/routes.yaml b/config/routes.yaml index c3283aa..b42047c 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,3 +1,4 @@ -#index: -# path: / -# controller: App\Controller\DefaultController::index +api_v1: + resource: ../src/Controller/Api/v1 + type: annotation + prefix: /{provider}/api/v1 diff --git a/config/services.yaml b/config/services.yaml index 279dc5f..57f7d5b 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,7 +4,7 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: - locale: 'en' + locale: 'pl' services: # default configuration for services in *this* file @@ -15,6 +15,10 @@ services: # fetching services directly from the container via $container->get() won't work. # The best practice is to be explicit about your dependencies anyway. + _instanceof: + App\Provider\Provider: + tags: [ app.provider ] + # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: @@ -46,8 +50,8 @@ services: proxy.config: class: 'ProxyManager\Configuration' calls: - - ['setGeneratorStrategy', ['@proxy.strategy']] - - ['setProxiesTargetDir', ['%kernel.cache_dir%/proxy']] + - ['setGeneratorStrategy', ['@proxy.strategy']] + - ['setProxiesTargetDir', ['%kernel.cache_dir%/proxy']] ProxyManager\Configuration: '@proxy.config' @@ -60,3 +64,7 @@ services: App\Service\Normalizer\: resource: '../src/Service/Normalizer' tags: [serializer.normalizer] + + # other servces + App\Service\ProviderResolver: + arguments: [!tagged app.provider] diff --git a/package.json b/package.json index ece12a7..0931b14 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,6 @@ "author": "Kacper Donat <kadet1090@gmail.com>", "license": "MIT", "devDependencies": { - "webpack": "^4.17.0", - "webpack-cli": "^3.1.0", "@fortawesome/fontawesome-svg-core": "^1.2.4", "@fortawesome/pro-light-svg-icons": "^5.3.1", "@fortawesome/pro-regular-svg-icons": "^5.3.1", @@ -30,7 +28,12 @@ "vue": "^2.5.17", "vue-class-component": "^6.2.0", "vue-property-decorator": "^7.0.0", + "webpack": "^4.17.0", + "webpack-cli": "^3.1.0", "xmldom": "^0.1.27", "xpath": "^0.0.27" + }, + "dependencies": { + "mini-css-extract-plugin": "^0.4.2" } } diff --git a/resources/components/departures.html b/resources/components/departures.html index 12f271b..5daf4a4 100644 --- a/resources/components/departures.html +++ b/resources/components/departures.html @@ -24,6 +24,10 @@ {{ departure.line.symbol }} </div> <div class="line__display">{{ departure.display }}</div> + <div class="line__perks flex-space-left"> + <fa :icon="['fas', 'walking']" fixed-width v-if="departure.line.fast"/> + <fa :icon="['fal', 'moon']" fixed-width v-if="departure.line.night"/> + </div> </div> <div class="departure__time"> diff --git a/resources/components/finder.html b/resources/components/finder.html index f9d9bb2..d2173b3 100644 --- a/resources/components/finder.html +++ b/resources/components/finder.html @@ -34,9 +34,11 @@ </div> </div> <div class="alert alert-warning" v-else-if="filter.length > 2"> + <fa :icon="['far', 'exclamation-triangle']"></fa> Nie znaleziono więcej przystanków, spełniających te kryteria. </div> <div class="alert alert-info" v-else> + <fa :icon="['far', 'search']"></fa> Wprowadź zapytanie powyżej, aby wyszukać przystanek. </div> </div> \ No newline at end of file diff --git a/resources/styles/_common.scss b/resources/styles/_common.scss index 27d2a1d..b9982a8 100644 --- a/resources/styles/_common.scss +++ b/resources/styles/_common.scss @@ -13,3 +13,19 @@ .flex-space-right { margin-right: auto; } + +.alert { + border-width: 0; + background: transparent; + + @each $color, $value in $theme-colors { + &.alert-#{$color} { + border-bottom: 2px solid theme-color-level($color, $alert-color-level); + transition: background-color ease 200ms; + + &:hover { + background-color: rgba(theme-color-level($color, $alert-bg-level), .5); + } + } + } +} diff --git a/resources/styles/main.scss b/resources/styles/main.scss index d3fea81..7359c14 100644 --- a/resources/styles/main.scss +++ b/resources/styles/main.scss @@ -1,4 +1,4 @@ -$border-radius: 2px; +$border-radius: 0; $border-radius-lg: $border-radius; $border-radius-sm: $border-radius; diff --git a/resources/ts/components/departures.ts b/resources/ts/components/departures.ts index 4d15123..4a0a398 100644 --- a/resources/ts/components/departures.ts +++ b/resources/ts/components/departures.ts @@ -23,7 +23,6 @@ export class Departures extends Vue { async update() { const response = await fetch(urls.prepare(urls.departures, { stop: this.stops.map(stop => stop.id), - provider: 'gdansk' })); if (response.ok) { diff --git a/resources/ts/components/picker.ts b/resources/ts/components/picker.ts index 4f5f6d4..0dc69c6 100644 --- a/resources/ts/components/picker.ts +++ b/resources/ts/components/picker.ts @@ -9,26 +9,11 @@ import stop = require('../../components/stop.html'); import { Prop, Watch } from "vue-property-decorator"; import { filter, map } from "../utils"; -import { debounce, throttle } from "../decorators"; -import { Departures } from "./departures"; +import { debounce } from "../decorators"; @Component({ template: picker }) export class PickerComponent extends Vue { - protected stops?: Stop[] = [{ - "id": 2001, - "name": "Dworzec Główny", - "description": null, - "location": [54.35544, 18.64565], - "variant": "01", - "onDemand": false - }, { - "id": 2002, - "name": "Dworzec Główny", - "description": null, - "location": [54.35541, 18.64548], - "variant": "02", - "onDemand": false - }]; + protected stops?: Stop[] = []; private remove(stop: Stop) { this.stops = this.stops.filter(s => s != stop); @@ -70,10 +55,7 @@ export class FinderComponent extends Vue { this.state = 'fetching'; - const response = await fetch(urls.prepare(urls.stops.search, { - name: this.filter, - provider: 'gdansk' - })); + const response = await fetch(urls.prepare(urls.stops.search, { name: this.filter })); if (response.ok) { this.found = await response.json(); diff --git a/resources/ts/urls.ts b/resources/ts/urls.ts index c75e86d..0114a69 100644 --- a/resources/ts/urls.ts +++ b/resources/ts/urls.ts @@ -45,12 +45,14 @@ export function prepare(url: string, params: UrlParams = { }) { return Object.keys(params).length > 0 ? `${url}?${query(params)}` : url; } +const base = '/{provider}/api/v1'; + export default { - departures: '/{provider}/departures', + departures: `${base}/departures`, stops: { - all: '/{provider}/stops', - search: '/{provider}/stops/search', - get: '/{provider}/stop/{id}' + all: `${base}/stops`, + search: `${base}/stops/search`, + get: `${base}/stops/{id}` }, - prepare + prepare: (url: string, params: UrlParams = { }) => prepare(url, Object.assign({}, { provider: window['app'].provider }, params)) } \ No newline at end of file diff --git a/src/Controller/DeparturesController.php b/src/Controller/Api/v1/DeparturesController.php similarity index 91% rename from src/Controller/DeparturesController.php rename to src/Controller/Api/v1/DeparturesController.php index a84a68d..8af7783 100644 --- a/src/Controller/DeparturesController.php +++ b/src/Controller/Api/v1/DeparturesController.php @@ -1,8 +1,9 @@ <?php -namespace App\Controller; +namespace App\Controller\Api\v1; +use App\Controller\Controller; use App\Model\Departure; use App\Provider\DepartureRepository; use App\Provider\StopRepository; @@ -12,7 +13,7 @@ use Symfony\Component\Routing\Annotation\Route; /** * Class DeparturesController * - * @Route("/{provider}/departures") + * @Route("/departures") */ class DeparturesController extends Controller { diff --git a/src/Controller/MessagesController.php b/src/Controller/Api/v1/MessagesController.php similarity index 78% rename from src/Controller/MessagesController.php rename to src/Controller/Api/v1/MessagesController.php index cd4e0ee..e7b821f 100644 --- a/src/Controller/MessagesController.php +++ b/src/Controller/Api/v1/MessagesController.php @@ -1,13 +1,14 @@ <?php -namespace App\Controller; +namespace App\Controller\Api\v1; +use App\Controller\Controller; use App\Provider\MessageRepository; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; /** - * @Route("/{provider}/messages") + * @Route("/messages") */ class MessagesController extends Controller { diff --git a/src/Controller/StopsController.php b/src/Controller/Api/v1/StopsController.php similarity index 92% rename from src/Controller/StopsController.php rename to src/Controller/Api/v1/StopsController.php index 609e75e..9494f30 100644 --- a/src/Controller/StopsController.php +++ b/src/Controller/Api/v1/StopsController.php @@ -1,8 +1,9 @@ <?php -namespace App\Controller; +namespace App\Controller\Api\v1; +use App\Controller\Controller; use App\Provider\StopRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; @@ -11,7 +12,7 @@ use Symfony\Component\Routing\Annotation\Route; * Class StopsController * * @package App\Controller - * @Route("/{provider}/stops") + * @Route("/stops") */ class StopsController extends Controller { diff --git a/src/Controller/HomepageController.php b/src/Controller/HomepageController.php deleted file mode 100644 index f4e53a4..0000000 --- a/src/Controller/HomepageController.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -namespace App\Controller; - - -use Symfony\Component\Routing\Annotation\Route; - -class HomepageController extends Controller -{ - /** - * @Route("/", name="home") - */ - public function homepage() - { - return $this->render('base.html.twig'); - } -} \ No newline at end of file diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php new file mode 100644 index 0000000..89a120a --- /dev/null +++ b/src/Controller/MainController.php @@ -0,0 +1,27 @@ +<?php + +namespace App\Controller; + + +use App\Provider\Provider; +use App\Service\ProviderResolver; +use Symfony\Component\Routing\Annotation\Route; + +class MainController extends Controller +{ + /** + * @Route("/", name="choose") + */ + public function choose(ProviderResolver $resolver) + { + return $this->render('choose.html.twig', ['providers' => $resolver->all()]); + } + + /** + * @Route("/{provider}", name="app") + */ + public function app(Provider $provider) + { + return $this->render('app.html.twig', ['provider' => $provider]); + } +} \ No newline at end of file diff --git a/src/Provider/Provider.php b/src/Provider/Provider.php index 3fd5a66..386ecc4 100644 --- a/src/Provider/Provider.php +++ b/src/Provider/Provider.php @@ -8,4 +8,7 @@ interface Provider public function getLineRepository(): LineRepository; public function getStopRepository(): StopRepository; public function getMessageRepository(): MessageRepository; + + public function getName(); + public function getIdentifier(); } \ No newline at end of file diff --git a/src/Provider/ZtmGdansk/ZtmGdanskDataUpdateSubscriber.php b/src/Provider/ZtmGdansk/ZtmGdanskDataUpdateSubscriber.php index 76f10cb..5d46f6a 100644 --- a/src/Provider/ZtmGdansk/ZtmGdanskDataUpdateSubscriber.php +++ b/src/Provider/ZtmGdansk/ZtmGdanskDataUpdateSubscriber.php @@ -27,6 +27,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface private $em; private $ids; private $logger; + private $provider; /** * ZtmGdanskDataUpdateSubscriber constructor. @@ -34,19 +35,24 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface * @param $provider * @param $em */ - public function __construct(EntityManagerInterface $em, IdUtils $ids, LoggerInterface $logger) - { - $this->em = $em; - $this->ids = $ids; - $this->logger = $logger; + public function __construct( + EntityManagerInterface $em, + IdUtils $ids, + LoggerInterface $logger, + ZtmGdanskProvider $provider + ) { + $this->em = $em; + $this->ids = $ids; + $this->logger = $logger; + $this->provider = $provider; } public function update() { $provider = ProviderEntity::createFromArray([ - 'name' => 'ZTM Gdańsk', + 'name' => $this->provider->getName(), 'class' => ZtmGdanskProvider::class, - 'id' => 'ztm-gdansk', + 'id' => $this->provider->getIdentifier(), ]); $this->em->persist($provider); @@ -94,7 +100,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface OperatorEntity::class, $this->ids->generate($provider, $line['agencyId']) ); - $type = [ + $type = [ 2 => LineModel::TYPE_TRAM, 5 => LineModel::TYPE_TROLLEYBUS, ]; diff --git a/src/Provider/ZtmGdanskProvider.php b/src/Provider/ZtmGdanskProvider.php index c4ae7db..12e9c8e 100644 --- a/src/Provider/ZtmGdanskProvider.php +++ b/src/Provider/ZtmGdanskProvider.php @@ -16,13 +16,23 @@ class ZtmGdanskProvider implements Provider private $stops; private $messages; + public function getName() + { + return 'MZKZG Trójmiasto'; + } + + public function getIdentifier() + { + return 'trojmiasto'; + } + public function __construct( EntityManagerInterface $em, GenericLineRepository $lines, GenericStopRepository $stops, ZtmGdanskMessageRepository $messages ) { - $provider = $em->getReference(ProviderEntity::class, 'ztm-gdansk'); + $provider = $em->getReference(ProviderEntity::class, $this->getIdentifier()); $lines = $lines->withProvider($provider); $stops = $stops->withProvider($provider); diff --git a/src/Service/EntityConverter.php b/src/Service/EntityConverter.php index a50d61f..9a2e3e2 100644 --- a/src/Service/EntityConverter.php +++ b/src/Service/EntityConverter.php @@ -64,6 +64,8 @@ final class EntityConverter 'symbol' => $entity->getSymbol(), 'type' => $entity->getType(), 'operator' => $convert($entity->getOperator()), + 'night' => $entity->isNight(), + 'fast' => $entity->isFast(), 'tracks' => $this->collection($entity->getTracks(), $convert), ]); break; diff --git a/src/Service/ProviderResolver.php b/src/Service/ProviderResolver.php index 9aebe2e..d793a14 100644 --- a/src/Service/ProviderResolver.php +++ b/src/Service/ProviderResolver.php @@ -3,30 +3,18 @@ namespace App\Service; - use App\Exception\NonExistentServiceException; use App\Provider\Provider; -use App\Provider\ZtmGdanskProvider; +use Kadet\Functional\Transforms as t; +use Tightenco\Collect\Support\Collection; class ProviderResolver { - private const PROVIDER = [ - 'gdansk' => ZtmGdanskProvider::class - ]; + private $providers; - /** - * @var \Symfony\Component\DependencyInjection\ContainerInterface - */ - private $container; - - /** - * ProviderResolver constructor. - * - * @param \Symfony\Component\DependencyInjection\ContainerInterface $container - */ - public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container) + public function __construct($providers) { - $this->container = $container; + $this->providers = collect($providers)->keyBy(t\property('identifier')); } /**\ @@ -37,10 +25,17 @@ class ProviderResolver */ public function resolve(string $name): Provider { - if (!array_key_exists($name, static::PROVIDER)) { - throw new NonExistentServiceException(); + if (!$this->providers->has($name)) { + $message = sprintf("Provider '%s' doesn't exist, you can choose from: %s", $name, $this->providers->keys()->implode(', ')); + throw new NonExistentServiceException($message); } - return $this->container->get(static::PROVIDER[$name]); + return $this->providers->get($name); + } + + /** @return Provider[] */ + public function all(): Collection + { + return clone $this->providers; } } \ No newline at end of file diff --git a/templates/app.html.twig b/templates/app.html.twig new file mode 100644 index 0000000..fe97015 --- /dev/null +++ b/templates/app.html.twig @@ -0,0 +1,13 @@ +{% extends 'base.html.twig' %} +{% block title "#{parent()} - #{provider.name}" %} +{% block body %} + <stop-picker></stop-picker> +{% endblock %} + +{% block javascripts %} + <script> + window.app = { + provider: {{ provider.identifier|json_encode|raw }} + } + </script> +{% endblock %} diff --git a/templates/base.html.twig b/templates/base.html.twig index 35aabd2..da1759e 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -1,16 +1,17 @@ <!DOCTYPE html> <html> <head> - <meta charset="UTF-8"> - <title>{% block title %}Welcome!{% endblock %}</title> - {% block stylesheets %}{% endblock %} + <meta charset="UTF-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" href="main.css" /> + + <title>{% block title %}Czy dojadę?{% endblock %}</title> </head> <body> - {% block body %} - <main id="app" class="container"> - <stop-picker></stop-picker> - </main> - {% endblock %} + <main role="main" class="container" id="app"> + {% block body %}{% endblock %} + </main> + <script src="bundle.js"></script> {% block javascripts %}{% endblock %} </body> diff --git a/templates/choose.html.twig b/templates/choose.html.twig new file mode 100644 index 0000000..a2108e1 --- /dev/null +++ b/templates/choose.html.twig @@ -0,0 +1,11 @@ +{% extends 'base.html.twig' %} + +{% block body %} + <div class="alert alert-primary"> + <fa :icon="['fal', 'info-circle']"></fa> + Wybierz źródło danych + </div> + {% for provider in providers %} + <a href="{{ path('app', { provider: provider.identifier }) }}">{{ provider.name }}</a> + {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index d65d5ca..aaa8d55 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,6 @@ const path = require('path'); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + const config = { entry: { main: ['./resources/ts/app.ts'], @@ -24,7 +26,9 @@ const config = { }] },{ test: /\.s[ac]ss$/, - use: ["style-loader", "css-loader?sourceMap", "sass-loader?sourceMap"] + use: [{ + loader: MiniCssExtractPlugin.loader, + }, "css-loader?sourceMap", "sass-loader?sourceMap"] }, { test: /\.css$/, use: ["style-loader", "css-loader"] @@ -46,6 +50,9 @@ const config = { use: 'raw-loader' }] }, + plugins: [ + new MiniCssExtractPlugin({ filename: '[name].css' }) + ], }; module.exports = (env, argv) => { diff --git a/yarn.lock b/yarn.lock index 2b8ea52..995e125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1967,6 +1967,14 @@ mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" +mini-css-extract-plugin@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.2.tgz#b3ecc0d6b1bbe5ff14add42b946a7b200cf78651" + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"