Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Cards #44

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,87 @@ class Employee extends Model implements Chartable
}
```

## Adding Cards

![Cards](screenshots/Cards.png "Cards")

You can add your Nova ChartJS charts as a card on the Dashboard or a Resource Index.

```php
use App\User as AppUser;
use KirschbaumDevelopment\NovaChartjs\Cards\NovaChartjsCard;

class User extends Resource
{
//...

/**
* Get the cards available for the request.
*
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function cards(Request $request)
{
return [
(new NovaChartjsCard())
->chartModel(AppUser::class)
->chartName('default')
->width('1/2'),
(new NovaChartjsCard())
->chartModel(AppUser::class)
->chartName('earnings')
->width('1/2'),
];
}

//...
}

```

Nova ChartJS automatically selects the dataset, settings, and additional datasets from your existing model and modifies it to suit the card format, but you can pass `settings`, `dataset`, and `additionalDatasets` to your chart.

```php
use App\User as AppUser;
use KirschbaumDevelopment\NovaChartjs\Cards\NovaChartjsCard;

class User extends Resource
{
//...

/**
* Get the cards available for the request.
*
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function cards(Request $request)
{
return [
(new NovaChartjsCard())
->chartModel(AppUser::class)
->chartName('default')
->settings([
'type' => 'bar',
'titleProp' => 'name',
'identProp' => 'id',
'height' => 250,
'indexColor' => '#999999',
'color' => '#FF0000',
'parameters' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
'options' => ['responsive' => true, 'maintainAspectRatio' => false],
])
->width('1/2'),
];
}

//...
}
```

## Settings

You can add following settings to model settings
Expand Down
2 changes: 1 addition & 1 deletion dist/js/field.js

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions resources/js/components/NovaChartjsCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<template>
<card class="p-10 nova-chartjs-card">
<div class="w-full py-4">
<chartjs-bar-chart v-if="isType('bar')"
:dataset="comparisonDataset"
:additionalDatasets="card.additionalDatasets"
:settings="card.settings"
:height="card.settings.height"
:width="card.settings.width"
/>
<chartjs-line-chart v-else
:dataset="comparisonDataset"
:additionalDatasets="card.additionalDatasets"
:settings="card.settings"
:height="card.settings.height"
:width="card.settings.width"
/>
Comment on lines +4 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to use a dynamic component here since everything is the same except for the component itself? Maybe something along these lines:

<component :is="componentType" v-bind="options"></component>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dvanscott This change is already a part of #41
Is it okay to handle it in that rebase?

</div>
</card>
</template>

<script>
import ChartjsLineChart from "./ChartjsLineChart";
import ChartjsBarChart from "./ChartjsBarChart";
import colors from "../mixins/colors";
import datasetHandler from "../mixins/datasetHandler";
export default {
components: {
ChartjsLineChart,
ChartjsBarChart
},

mixins: [colors, datasetHandler],

props: ['card'],

data() {
return {
selected: []
}
},

methods: {
isType: function(type){
return this.card.settings.type.toLowerCase() === type
},

getDatapoint: function(values, title, color){
if(!color){
color = this.getRandomColor();
}

if(!values || (typeof values != 'object')){
values = [];
}

return {
label: title,
data: this.getAllowedParametersFromDataset(this.card.settings.parameters, values),
...this.getChartTypeCustomizations(this.card.settings.type, color)
}
},

getChartTypeCustomizations: function(type, color){
if(this.isType('line')){
return {
borderColor: color
}
}else{
return {
backgroundColor: color
}
}
}
},

computed: {
comparisonDataset: function(){
return [
...this.card.dataset.map(
data => this.getDatapoint(
data.novaChartjsComparisonData,
data[this.card.settings.titleProp]
)
)
];
},
}

}
</script>
<style lang="scss" scoped>
.nova-chartjs-card {
height: auto !important;
min-height: 150px;
}
</style>
1 change: 1 addition & 0 deletions resources/js/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ Nova.booting(Vue => {
Vue.component('index-nova-chartjs', require('./components/IndexField'));
Vue.component('detail-nova-chartjs', require('./components/DetailField'));
Vue.component('form-nova-chartjs', require('./components/FormField'));
Vue.component('card-nova-chartjs', require('./components/NovaChartjsCard'));
});
Binary file added screenshots/Cards.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 103 additions & 0 deletions src/Cards/NovaChartJsCard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace KirschbaumDevelopment\NovaChartjs\Cards;

use Laravel\Nova\Card;

class NovaChartjsCard extends Card
{
/**
* The width of the card (1/3, 1/2, or full).
*
* @var string
*/
public $width = 'full';

public function __construct($component = null)
{
parent::__construct($component);
}

/**
* Get the component name for the element.
*
* @return string
*/
public function component()
{
return 'card-nova-chartjs';
}

public function settings(array $settings): self
{
return $this->withMeta(['settings' => $settings]);
}

public function chartModel(string $model): self
{
return $this->withMeta(['chartModel' => $model]);
}

public function additionalDatasets(array $additionalDatasets): self
{
return $this->withMeta(['additionalDatasets' => $additionalDatasets]);
}

public function dataset(array $dataset): self
{
return $this->withMeta(['dataset' => $dataset]);
}

/**
* Set chart name for the chart.
*
* @param string $chartName
*
* @return NovaChartjs
*/
public function chartName($chartName = 'default'): self
{
return $this->withMeta([
'chartName' => $chartName,
]);
}

/**
* Prepare the element for JSON serialization.
*
* @return array
*/
public function jsonSerialize()
{
$this->updateChartMeta();

return parent::jsonSerialize();
}

/**
* Returns chartname for current chart.
*
* @return string
*/
protected function getChartName()
{
return data_get($this->meta(), 'chartName', 'default');
}

/**
* Updates Meta values for Charts.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function updateChartMeta()
{
$model = app(data_get($this->meta(), 'chartModel'));
$chartName = $this->getChartName();

$this->withMeta([
'dataset' => data_get($this->meta(), 'dataset', $model::getCardDatasets($chartName)),
'settings' => data_get($this->meta(), 'settings', $model::getNovaChartjsCardSettings($chartName)),
'additionalDatasets' => data_get($this->meta(), 'additionalDatasets', $model::getAdditionalCardDatasets($chartName)),
]);
}
}
2 changes: 2 additions & 0 deletions src/NovaChartjs.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ protected function fillAttributeFromRequest(NovaRequest $request, $requestAttrib
if ($model instanceof NovaChartjsMetricValue) {
$value = json_decode($request[$requestAttribute], true);
$model->{$attribute} = $this->isNullValue($value) ? null : $value;

return;
}

$chartName = $this->getChartName();
Expand Down
51 changes: 51 additions & 0 deletions src/Traits/HasChart.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,55 @@ public function getAdditionalDatasets(): array
'default' => [],
];
}

/**
* Return a list of all models available to be shown in a card.
*
* @param string $chartName
* @param string $cardName
*
* @return array
*/
public static function getCardDatasets($chartName = 'default', $cardName = ''): array
{
return self::getNovaChartjsComparisonData($chartName);
}

/**
* Should return settings for Nova Chart in prescribed format.
*
* @param mixed $chartName
*
* @return array
*/
public static function getNovaChartjsCardSettings($chartName = 'default'): array
{
return array_merge(
[
'type' => 'bar',
'titleProp' => 'name',
'identProp' => 'id',
'indexColor' => '#999999',
'color' => '#FF0000',
'options' => ['responsive' => true, 'maintainAspectRatio' => false],
],
data_get(self::getNovaChartjsSettings(), $chartName, []),
[
'height' => 200,
'options' => ['responsive' => true, 'maintainAspectRatio' => false, 'legend' => ['position' => 'left', 'align' => 'middle']],
]
);
}

/**
* Return a list of additional datasets added to card.
*
* @param mixed $chartName
*
* @return array
*/
public static function getAdditionalCardDatasets($chartName = 'default'): array
{
return data_get((new self())->getAdditionalDatasets(), $chartName, []);
}
}