diff --git a/Plugin.php b/Plugin.php index 7c9b15c..176cf1d 100644 --- a/Plugin.php +++ b/Plugin.php @@ -8,11 +8,6 @@ class Plugin extends \MapasCulturais\Plugin { function __construct($config = []) { - $config += [ - 'links' => [], - 'cards' => [], - ]; - parent::__construct($config); } @@ -20,19 +15,39 @@ public function _init() { $app = App::i(); //load css - $app->hook('<>(<>.<<*>>)', function() use ($app) { + $app->hook('<>(<<*>>.<<*>>)', function() use ($app) { $app->view->enqueueStyle('app-v2', 'metabase', 'css/plugin-metabase.css'); }); - $app->hook("component(home-feature):after", function() { + $app->hook('component(home-feature):after', function() { /** @var \MapasCulturais\Theme $this */ $this->part('home-metabase'); }); + $app->hook('template(search.agents.search-tabs):after', function(){ + $this->part('search-tabs/agent'); + }); + + // $app->hook('template(search.spaces.search-tabs):after', function(){ + // $this->part('search-tabs/space'); + // }); + + $app->hook('template(search.agents.search-header):after', function(){ + $this->part('search-tabs/entity-agent-cards'); + }); + + // $app->hook('template(search.spaces.search-header):after', function(){ + // $this->part('search-tabs/entity-space-cards'); + // }); + $self= $this; - $app->hook("app.init:after", function() use ($self){ + $app->hook('app.init:after', function() use ($self){ $this->view->metabasePlugin = $self; }); + $app->hook('component(mc-icon).iconset', function(&$iconset) { + $iconset['indicator'] = 'cil:chart-line'; + }); + } public function register() diff --git a/README.md b/README.md index b3b8c74..287f0eb 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,48 @@ Criação do arquivo de configuração do plugin Metabase.php no diretório conf 'title' => 'TITULO_DASHBOARD' ], ], + 'cards' => [ + // cards que aparecem na home + 'home' => [ + [ + 'type' => 'agent', + 'label' => '', + 'icon'=> 'agent', + 'iconClass'=> 'agent__color', + 'panelLink'=> 'painel-agentes', + 'data'=> [ + [ + 'icon'=> 'agent', + 'label' => 'Agentes cadastrados', + 'entity' => 'MapasCulturais\\Entities\\Agent', + 'query' => [], + 'value' => null + ], + ] + ], + ], + // cards que aparecem na entidade (página de pesquisa) + 'entities' => [ + [ + 'type' => 'agent', + 'label' => '', + 'icon'=> 'agent', + 'iconClass'=> 'agent__color', + 'panelLink'=> 'painel-agentes', + 'data'=> [ + [ + 'id' => 'agentes-cadastrados', + 'icon'=> 'agent', + 'label' => 'Agentes cadastrados', + 'entity' => 'MapasCulturais\\Entities\\Agent', + 'query' => [], + 'value' => null + ], + ] + ], + ] + ] ] ] ]; -``` +``` \ No newline at end of file diff --git a/assets-src/sass/4-components/_entity-cards.scss b/assets-src/sass/4-components/_entity-cards.scss new file mode 100644 index 0000000..f03dc33 --- /dev/null +++ b/assets-src/sass/4-components/_entity-cards.scss @@ -0,0 +1,126 @@ +@use '../2-atoms/a-mixins' as *; + + +.entity-cards { + margin: 0 auto; + position: relative; + display: flex; + align-items: center; + overflow: hidden; + flex-direction: column; + padding: 2px; + + &__header { + margin: 0 auto; + width: 100%; + display: grid; + align-items: center; + grid-template-columns: 1fr size(455); + + @media (max-width:size(600)) { + display: flex; + flex-direction: column; + align-items: center; + } + } + + &__panel { + display: flex; + flex-direction: column; + gap: size(12); + } + + + &__content { + align-items: stretch; + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 0 auto; + max-width: size(1170); + z-index: 2; + gap: size(18) size(13); + + + @media (max-width:size(500)) { + gap: size(18) 0; + } + } +} + +.entity-cards-cards { + width: size(220); + max-width: 100%; + background-color: var(--mc-white); + border: size(2) solid var(--mc-gray-100); + box-shadow: 2px 2px 8px 0px #00000040; + // border: size(1) solid red; + border-radius: size(8); + display: grid; + align-content: space-between; + padding: size(5); + + &__header { + display: grid; + grid-template-columns: size(32) 1fr; + gap: size(16); + } + &__icon { + background-color: var(--mc-white); + align-self:self-start; + color: #EF7B45; + } + + &__content { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: end; + text-align: center; + border-radius: size(8); + + } + + &__number { + font-size: var(--mc-font-size-lg) + } + + &__label { + + color: #666666; + font-size: var(--mc-font-size-xxxs); + } + + &__info { + display: flex; + flex-direction: column; + justify-content: space-between; + + align-items: left; + text-align: left; + margin-left: size(2); + } + + &--double & { + &__content { + grid-template-columns: 1fr 1fr; + } + + &__info:first-of-type .metabase-card__number { + font-size: var(--mc-font-size-lg); + + &--long { + font-size: var(--mc-font-size-sm); + } + } + + &__number { + font-size: var(--mc-font-size-md); + } + + } + @media (max-width:size(500)){ + min-width: size(297); + width: 100%; + } +} \ No newline at end of file diff --git a/assets-src/sass/plugin-metabase.scss b/assets-src/sass/plugin-metabase.scss index 4a9a89b..69d9690 100755 --- a/assets-src/sass/plugin-metabase.scss +++ b/assets-src/sass/plugin-metabase.scss @@ -17,6 +17,7 @@ @import '4-components/metabase-dashboard'; @import '4-components/metabase-card'; @import '4-components/single-dashboard'; +@import '4-components/entity-cards'; /* ----- 5: AREAS ------- */ \ No newline at end of file diff --git a/assets/css/plugin-metabase.css b/assets/css/plugin-metabase.css new file mode 100644 index 0000000..ccccaef --- /dev/null +++ b/assets/css/plugin-metabase.css @@ -0,0 +1 @@ +.list-dashboard{background-color:var(--mc-white);color:var(--mc-black);display:grid;grid-template-columns:19.0625rem calc(100% - 19.0625rem);margin:0 auto;max-width:90rem}.list-dashboard .selected{background-color:var(--mc-primary-500)}.list-dashboard__title{color:var(--mc-gray-700);font-size:.75rem;margin:0 auto;padding:1rem 1rem 1rem 0;text-transform:uppercase}.list-dashboard__text{color:var();font-size:.875rem}.list-dashboard .textselected{color:var(--mc-white)}.list-dashboard__link{color:var(--mc-black);text-decoration:none}.list-dashboard__link a{text-decoration:none}.list-dashboard__nav{background-color:var(--mc-white);border-right:1px solid var(--mc-gray-100);display:flex;flex-direction:column;gap:.5rem;list-style-type:none;margin:0;padding:1rem 2rem 1rem 1.5rem}.list-dashboard__iframe{border-style:none;height:100%;width:100%}.list-dashboard__item{background-color:var(--mc-white);border-radius:.25rem;color:var(--mc-black);margin:0;padding:.5rem 1rem;width:15.625rem}.home-metabase{align-items:center;background-color:var(--mc-home-opportunities);display:flex;flex-direction:column;margin:0 auto;overflow:hidden;padding:3.75rem 1rem;position:relative}.home-metabase__header{align-items:center;display:grid;grid-template-columns:1fr 28.4375rem;margin:0 auto;max-width:73.125rem;width:100%}@media (max-width:37.5rem){.home-metabase__header{align-items:center;display:flex;flex-direction:column}}.home-metabase__left{align-items:flex-start;color:var(--mc-white);display:flex;flex-direction:column;gap:1rem}@media (max-width:37.5rem){.home-metabase__left{align-items:center}.home-metabase__text{text-align:center}}.home-metabase__img{max-width:100%;width:17.8125rem}.home-metabase__panel{display:flex;flex-direction:column;gap:.75rem}.home-metabase__subtitle{color:var(--mc-white)}.home-metabase__content{align-items:stretch;display:flex;flex-direction:row;flex-wrap:wrap;gap:1.125rem .8125rem;margin:0 auto;max-width:73.125rem;z-index:2}@media (max-width:31.25rem){.home-metabase__content{gap:1.125rem 0}}.metabase-dashboard{background-color:var(--mc-gray-100);margin:0 auto;max-width:90rem;padding-inline:7.4375rem}@media (max-width:31.25rem){.metabase-dashboard{padding-inline:1rem}}.metabase-dashboard__page{background-color:var(--mc-gray-100)}.metabase-dashboard__header{display:flex;flex-direction:column;padding:2.5rem 0 1rem}.metabase-dashboard__content{display:flex;flex-direction:row;flex-wrap:wrap;gap:1.5rem;padding-bottom:13.5rem}.metabase-dashboard__card{align-items:flex-start;background:var(--mc-white);border:1px solid var(--mc-gray-100);border-radius:8px;display:inline-flex;flex-direction:column;gap:6px;max-width:23.0625rem;padding:16px 35px 16px 16px}.metabase-card{align-content:space-between;background-color:var(--mc-white);border:.0625rem solid var(--mc-gray-100);border-radius:.5rem;display:grid;max-width:100%;padding:1rem;width:23.1875rem}.metabase-card__header{display:grid;gap:1rem;grid-template-columns:2rem 1fr}.metabase-card__icon{background-color:var(--mc-white);font-size:1.5rem;justify-self:center}.metabase-card__content{align-items:end;display:grid;gap:1.625rem;grid-template-columns:1fr;text-align:center}.metabase-card__number{font-size:var(--mc-font-size-lg)}.metabase-card__label{font-weight:600}.metabase-card__info{align-items:center;display:flex;flex-direction:column;margin:1.125rem 0;text-align:center}.metabase-card__number{font-size:var(--mc-font-size-xl)}.metabase-card__number--long{font-size:var(--mc-font-size-sm)}.metabase-card--double .metabase-card__content{grid-template-columns:1fr 1fr}.metabase-card--double .metabase-card__info:first-of-type .metabase-card__number{font-size:var(--mc-font-size-lg)}.metabase-card--double .metabase-card__info:first-of-type .metabase-card__number--long{font-size:var(--mc-font-size-sm)}.metabase-card--double .metabase-card__number{font-size:var(--mc-font-size-md)}@media (max-width:31.25rem){.metabase-card{min-width:18.5625rem;width:100%}}.controller-metabase{background-color:var(--mc-gray-100)}.entity-cards{align-items:center;display:flex;flex-direction:column;margin:0 auto;overflow:hidden;padding:2px;position:relative}.entity-cards__header{align-items:center;display:grid;grid-template-columns:1fr 28.4375rem;margin:0 auto;width:100%}@media (max-width:37.5rem){.entity-cards__header{align-items:center;display:flex;flex-direction:column}}.entity-cards__panel{display:flex;flex-direction:column;gap:.75rem}.entity-cards__content{align-items:stretch;display:flex;flex-direction:row;flex-wrap:wrap;gap:1.125rem .8125rem;margin:0 auto;max-width:73.125rem;z-index:2}@media (max-width:31.25rem){.entity-cards__content{gap:1.125rem 0}}.entity-cards-cards{align-content:space-between;background-color:var(--mc-white);border:.125rem solid var(--mc-gray-100);border-radius:.5rem;box-shadow:2px 2px 8px 0 rgba(0,0,0,.251);display:grid;max-width:100%;padding:.3125rem;width:13.75rem}.entity-cards-cards__header{display:grid;gap:1rem;grid-template-columns:2rem 1fr}.entity-cards-cards__icon{align-self:self-start;background-color:var(--mc-white);color:#ef7b45}.entity-cards-cards__content{align-items:end;border-radius:.5rem;display:flex;flex-direction:row;justify-content:space-between;text-align:center}.entity-cards-cards__number{font-size:var(--mc-font-size-lg)}.entity-cards-cards__label{color:#666;font-size:var(--mc-font-size-xxxs)}.entity-cards-cards__info{align-items:left;display:flex;flex-direction:column;justify-content:space-between;margin-left:.125rem;text-align:left}.entity-cards-cards--double .entity-cards-cards__content{grid-template-columns:1fr 1fr}.entity-cards-cards--double .entity-cards-cards__info:first-of-type .metabase-card__number{font-size:var(--mc-font-size-lg)}.entity-cards-cards--double .entity-cards-cards__info:first-of-type .metabase-card__number--long{font-size:var(--mc-font-size-sm)}.entity-cards-cards--double .entity-cards-cards__number{font-size:var(--mc-font-size-md)}@media (max-width:31.25rem){.entity-cards-cards{min-width:18.5625rem;width:100%}} diff --git a/assets/mix-manifest.json b/assets/mix-manifest.json deleted file mode 100644 index 2d60117..0000000 --- a/assets/mix-manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "/js/app.js": "/js/app.js", - "/css/app.css": "/css/app.css" -} diff --git a/components/entity-cards/README.md b/components/entity-cards/README.md new file mode 100644 index 0000000..6ae8199 --- /dev/null +++ b/components/entity-cards/README.md @@ -0,0 +1,47 @@ +# Componente `<__template__>` +A ideia do componente `__template__` é servir de modelo para a criação de novos componentes. Este implementa um exemplo +de componente que manipula um objeto do tipo `Entity` e dispara um evento quando este for modificado. + +Este documento (README.md) deve conter a descrição do que o componente faz e toda a interface pública do componente. + +Neste exemplo, o componente recebe obrigatoriamente um nome e uma entidade e opcionalmente um apelido (nickname) e um sobrenome. +Baseado nos parâmetros passados para o componente, este gera um `displayName` e um `fullname`. + +Quando o botão "Definir Nomes" é clicado, este definirá o `entity.name` e `entity.nomeCompleto` com o valores `displaName` e `fullname`, +respectivamente, e emitirá o evento `namesDefined`, que poderá ser capturado fora do componente. + +### Eventos +- **namesDefined** - disparado quando o método `defineNames` é chamado, após a definição do `name` e `nomeCompleto` + +## Propriedades +- *Entity **entity*** - Entidade +- *String **name*** - Nome +- *String **lastname** = ''* - Sobrenome + +## Slots +- **default** `{fullname, displayName, compareDisplayName, compareFullname}`: informações dos nomes e comparação com o nome gerado + +### Importando componente +```PHP +import('__template__'); +?> +``` +### Exemplos de uso +```HTML + +<__template__ :entity="entity" name="Fulano"> + + +<__template__ :entity="entity" name="Fulano" nickname="Lano" @names-defined="entity.save()"> + + +<__template__ :entity="entity" name="Fulano" nickname="Lano" @names-defined="entity.save()" #default="props"> +
o nome está atualizado
+
o novo nome será {{props.displayName}}
+ +
o nome completo está atualizado
+
o novo nome completo será {{props.fullname}}
+ + +``` \ No newline at end of file diff --git a/components/entity-cards/init.php b/components/entity-cards/init.php new file mode 100644 index 0000000..3bdc88d --- /dev/null +++ b/components/entity-cards/init.php @@ -0,0 +1,25 @@ +config['Metabase']['config']['cards']['entities']; + +$app->applyHook('component(home-metabase).data', [&$cards]); + +foreach ($cards as &$card) { + foreach ($card['data'] as &$data) { + $query = $data['query']; + $entity = $data['entity']; + $api_query = new ApiQuery($entity, $query); + $data['data'] = $api_query->getFindResult(); + $data['value'] = $api_query->getCountResult(); + } +} + +$this->jsObject['config']['homeMetabase'] = $cards; + +return; + diff --git a/components/entity-cards/script.js b/components/entity-cards/script.js new file mode 100644 index 0000000..dc8d96a --- /dev/null +++ b/components/entity-cards/script.js @@ -0,0 +1,61 @@ +app.component('entity-cards', { + template: $TEMPLATES['entity-cards'], + + props: { + classes: { + type: [String, Array, Object], + required: false + }, + type: { + type: String, + default: '', + }, + }, + + setup({ slots }) { + const hasSlot = name => !!slots[name]; + const text = Utils.getTexts('entity-cards') + return { text, hasSlot } + }, + + data() { + const cards = $MAPAS.config.homeMetabase.filter((c) => { + return c.type == this.type + }); + + cards.map((c) => { + c.data.map((d) => { + if (d.id == 'agentes-cadastrados-7-dias') { + const today = new Date(); + const sevenDaysBefore = new Date(); + sevenDaysBefore.setDate(today.getDate() - 7); + + const newData = d.data.filter(dd => { + const dateObject = new Date(dd.createTimestamp.date); + return dateObject >= sevenDaysBefore && dateObject <= today; + }); + + d.value = newData.length; + return d; + } + + return d; + }); + }); + + return { + cards: cards, + } + }, + + computed: { }, + + methods: { + lengthClass(text) { + const textString = String(text); + if (textString.length > 5) { + return 'metabase-card__number--long'; + } + }, + }, +}); diff --git a/components/entity-cards/template.php b/components/entity-cards/template.php new file mode 100644 index 0000000..b2a9aab --- /dev/null +++ b/components/entity-cards/template.php @@ -0,0 +1,29 @@ +import(' + mc-link + mc-icon +'); + +?> + +
+
+
+
+
+
+ {{data.value}} + +
+
+
+
+
+
+
\ No newline at end of file diff --git a/components/entity-cards/texts.php b/components/entity-cards/texts.php new file mode 100644 index 0000000..2e01a92 --- /dev/null +++ b/components/entity-cards/texts.php @@ -0,0 +1,5 @@ +metabasePlugin->config['cards']; + +$cards = $app->config['Metabase']['config']['cards']['home']; $app->applyHook('component(home-metabase).data', [&$cards]); diff --git a/components/list-dashboard/init.php b/components/list-dashboard/init.php index 0ac37cb..c7187f7 100644 --- a/components/list-dashboard/init.php +++ b/components/list-dashboard/init.php @@ -1,5 +1,5 @@ jsObject['config']['listDashboard'] = [ - 'links'=> $app->config['plugins']['Metabase']['config']['links'], + 'links'=> $app->config['Metabase']['config']['links'], ]; diff --git a/components/metabase-dashboard/init.php b/components/metabase-dashboard/init.php index 3b36eed..5518b02 100644 --- a/components/metabase-dashboard/init.php +++ b/components/metabase-dashboard/init.php @@ -1,5 +1,5 @@ jsObject['config']['metabaseDashboard'] = [ - 'links'=> $app->config['plugins']['Metabase']['config']['links'], + 'links'=> $app->config['Metabase']['config']['links'], ]; diff --git a/components/search-dashboard/README.md b/components/search-dashboard/README.md new file mode 100644 index 0000000..6ae8199 --- /dev/null +++ b/components/search-dashboard/README.md @@ -0,0 +1,47 @@ +# Componente `<__template__>` +A ideia do componente `__template__` é servir de modelo para a criação de novos componentes. Este implementa um exemplo +de componente que manipula um objeto do tipo `Entity` e dispara um evento quando este for modificado. + +Este documento (README.md) deve conter a descrição do que o componente faz e toda a interface pública do componente. + +Neste exemplo, o componente recebe obrigatoriamente um nome e uma entidade e opcionalmente um apelido (nickname) e um sobrenome. +Baseado nos parâmetros passados para o componente, este gera um `displayName` e um `fullname`. + +Quando o botão "Definir Nomes" é clicado, este definirá o `entity.name` e `entity.nomeCompleto` com o valores `displaName` e `fullname`, +respectivamente, e emitirá o evento `namesDefined`, que poderá ser capturado fora do componente. + +### Eventos +- **namesDefined** - disparado quando o método `defineNames` é chamado, após a definição do `name` e `nomeCompleto` + +## Propriedades +- *Entity **entity*** - Entidade +- *String **name*** - Nome +- *String **lastname** = ''* - Sobrenome + +## Slots +- **default** `{fullname, displayName, compareDisplayName, compareFullname}`: informações dos nomes e comparação com o nome gerado + +### Importando componente +```PHP +import('__template__'); +?> +``` +### Exemplos de uso +```HTML + +<__template__ :entity="entity" name="Fulano"> + + +<__template__ :entity="entity" name="Fulano" nickname="Lano" @names-defined="entity.save()"> + + +<__template__ :entity="entity" name="Fulano" nickname="Lano" @names-defined="entity.save()" #default="props"> +
o nome está atualizado
+
o novo nome será {{props.displayName}}
+ +
o nome completo está atualizado
+
o novo nome completo será {{props.fullname}}
+ + +``` \ No newline at end of file diff --git a/components/search-dashboard/init.php b/components/search-dashboard/init.php new file mode 100644 index 0000000..c7187f7 --- /dev/null +++ b/components/search-dashboard/init.php @@ -0,0 +1,5 @@ +jsObject['config']['listDashboard'] = [ + 'links'=> $app->config['Metabase']['config']['links'], + + ]; diff --git a/components/search-dashboard/script.js b/components/search-dashboard/script.js new file mode 100644 index 0000000..ea843a5 --- /dev/null +++ b/components/search-dashboard/script.js @@ -0,0 +1,50 @@ +const iFrameResizeModule = import('https://unpkg.com/iframe-resizer@4.3.1/js/iframeResizer.min.js'); +app.component('search-dashboard', { + template: $TEMPLATES['search-dashboard'], + + setup({ slots }) { + const hasSlot = name => !!slots[name]; + // os textos estão localizados no arquivo texts.php deste componente + const text = Utils.getTexts('search-dashboard') + return { text, hasSlot } + }, + props: { + panelId: { + type: String, + default: '', + }, + }, + mounted() { + // this.$refs.dashboardIframe.src = this.links[this.panelId].link; + iFrameResizeModule.then(() => { + iFrameResize({ log: false }, this.$refs.dashboardIframe); + }); + }, + + data() { + return {}; + }, + + computed: { + link() { + return this.links[this.panelId].link; + }, + links() { + return $MAPAS.config.listDashboard.links; + }, + names() { + const result = []; + Object.keys(this.links).forEach(name => { + result.push(name); + }) + return result; + }, + }, + + methods: { + getUrl(name) { + let url = Utils.createUrl('metabase', 'dashboard', { panelId: name }); + return url; + } + }, +}); diff --git a/components/search-dashboard/template.php b/components/search-dashboard/template.php new file mode 100644 index 0000000..2c9a417 --- /dev/null +++ b/components/search-dashboard/template.php @@ -0,0 +1,18 @@ +import(' + mc-icon + mc-link +'); + +?> +
+ +
\ No newline at end of file diff --git a/components/search-dashboard/texts.php b/components/search-dashboard/texts.php new file mode 100644 index 0000000..90629fb --- /dev/null +++ b/components/search-dashboard/texts.php @@ -0,0 +1,6 @@ +import(' + search-dashboard +'); + +?> + +
+ +
+
\ No newline at end of file diff --git a/layouts/parts/search-tabs/entity-agent-cards.php b/layouts/parts/search-tabs/entity-agent-cards.php new file mode 100644 index 0000000..e582d07 --- /dev/null +++ b/layouts/parts/search-tabs/entity-agent-cards.php @@ -0,0 +1,14 @@ +import(' + entity-cards +'); +?> + \ No newline at end of file diff --git a/layouts/parts/search-tabs/entity-space-cards.php b/layouts/parts/search-tabs/entity-space-cards.php new file mode 100644 index 0000000..0d6fd6f --- /dev/null +++ b/layouts/parts/search-tabs/entity-space-cards.php @@ -0,0 +1,14 @@ +import(' + entity-cards +'); +?> + \ No newline at end of file diff --git a/layouts/parts/search-tabs/space.php b/layouts/parts/search-tabs/space.php new file mode 100644 index 0000000..a051896 --- /dev/null +++ b/layouts/parts/search-tabs/space.php @@ -0,0 +1,19 @@ +import(' + search-dashboard +'); + +?> + +
+ +
+
\ No newline at end of file diff --git a/mix-manifest.json b/mix-manifest.json new file mode 100644 index 0000000..95dd057 --- /dev/null +++ b/mix-manifest.json @@ -0,0 +1,3 @@ +{ + "/assets/css/plugin-metabase.css": "/assets/css/plugin-metabase.css" +}