dcasia / nova-dashboard Goto Github PK
View Code? Open in Web Editor NEWThe missing dashboard for Laravel Nova!
License: MIT License
The missing dashboard for Laravel Nova!
License: MIT License
Docs were quickly written, there are grammar mistakes.. and many things are missing such as
I can get filter value from Laravel\Nova\Filters\DateFilter
but can not get it from Laravel\Nova\Filters\Filter
class. it returns null value!
Did you find a trick to replace default dashboard with this one using digital-creative/collapsible-resource-manager?
You forgot this line in README. Quite important !
Hi,
After updating the the latest version ( 2b82fea )
While all my value filters are working well, my chart now has a problem. If I change a filter, chart will not update. I checked that all queries are being well executed ( the changing params are OK).
The latest commit that is working for me is :
After that, chart is not taking the filters modification.
running
php artisan vendor:publish --provider="DigitalCreative\NovaDashboard\ToolServiceProvider" --tag="migration"
fails with
Unable to locate publishable resources.
However php artisan migrate
works
Love the changes you made, however I'm getting a few errors on the value widget now.
Not sure if it's me though, you git it working in combination with nova-dashboard?
Error in render: "TypeError: Cannot read property 'Increase' of null"
found in
---> <Widget> at resources/js/components/Widget.vue
Missing required prop: "card"
found in
---> <Widget> at resources/js/components/Widget.vue
Hi,
I have only 1 dashboard, I would like to hide the whole dashboard items ( Dashboards and subitem my-dashboard ).
Is it currently possible to hide the whole dashboard without losing dashboard ( if I remove it from NovaServiceProvider, it also disappear )
Hey @milewski,
any plans to support Nova 4?
The Nova 4 compatibility of the dashboard manager is depending on this package, see Nova-BI/nova-dashboard-manager#29
Hi,
I'm using the plugin https://github.com/awesome-nova/dependent-filter#dynamic-dependencies as I have operation, and inside operations, I have meters.
So when I select an operation, I want meter filter to refresh, filtering by operation.
While I could implement it in resource views, it is not working in dashboard, I get a 404 on http://localhost:8000/nova-api/tableau-de-bord/filters/options?filters=e30%3D&filter=operation_id
{
"message": "",
"exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
"line": 1068,
"trace": [
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",
"line": 44,
"function": "abort",
"class": "Illuminate\\Foundation\\Application",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",
"line": 64,
"function": "abort"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Requests/InteractsWithResources.php",
"line": 51,
"function": "abort_if"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Support/helpers.php",
"line": 263,
"function": "Laravel\\Nova\\Http\\Requests\\{closure}",
"class": "Laravel\\Nova\\Http\\Requests\\NovaRequest",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Requests/InteractsWithResources.php",
"line": 53,
"function": "tap"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Requests/InteractsWithResources.php",
"line": 63,
"function": "resource",
"class": "Laravel\\Nova\\Http\\Requests\\NovaRequest",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/awesome-nova/dependent-filter/src/Http/Controllers/FilterController.php",
"line": 15,
"function": "newResource",
"class": "Laravel\\Nova\\Http\\Requests\\NovaRequest",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
"line": 48,
"function": "options",
"class": "AwesomeNova\\Http\\Controllers\\FilterController",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 255,
"function": "dispatch",
"class": "Illuminate\\Routing\\ControllerDispatcher",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 197,
"function": "runController",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 691,
"function": "run",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 128,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Middleware/Authorize.php",
"line": 18,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Laravel\\Nova\\Http\\Middleware\\Authorize",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Middleware/BootTools.php",
"line": 20,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Laravel\\Nova\\Http\\Middleware\\BootTools",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Middleware/DispatchServingNovaEvent.php",
"line": 20,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Laravel\\Nova\\Http\\Middleware\\DispatchServingNovaEvent",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",
"line": 44,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Middleware/Authenticate.php",
"line": 31,
"function": "handle",
"class": "Illuminate\\Auth\\Middleware\\Authenticate",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Laravel\\Nova\\Http\\Middleware\\Authenticate",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
"line": 41,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php",
"line": 77,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php",
"line": 49,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\View\\Middleware\\ShareErrorsFromSession",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
"line": 121,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
"line": 63,
"function": "handleStatefulRequest",
"class": "Illuminate\\Session\\Middleware\\StartSession",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Session\\Middleware\\StartSession",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php",
"line": 37,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php",
"line": 67,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Cookie\\Middleware\\EncryptCookies",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 103,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 693,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 668,
"function": "runRouteWithinStack",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 634,
"function": "runRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 623,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 166,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 128,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/kabbouchi/nova-impersonate/src/Http/Middleware/Impersonate.php",
"line": 23,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "KABBOUCHI\\NovaImpersonate\\Http\\Middleware\\Impersonate",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/nova/src/Http/Middleware/ServeNova.php",
"line": 26,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Laravel\\Nova\\Http\\Middleware\\ServeNova",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php",
"line": 67,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/spatie/laravel-cookie-consent/src/CookieConsentMiddleware.php",
"line": 12,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Spatie\\CookieConsent\\CookieConsentMiddleware",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
"line": 87,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/fideloper/proxy/src/TrustProxies.php",
"line": 57,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 167,
"function": "handle",
"class": "Fideloper\\Proxy\\TrustProxies",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 103,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 141,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/home/julio/Code/acc/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 110,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/home/julio/Code/acc/public/index.php",
"line": 55,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/home/julio/Code/acc/server.php",
"line": 21,
"function": "require_once"
}
]
}
For resource page, I get a Code 200:
[{"label":"ACC00000017","value":1},{"label":"ACC00000021","value":2},{"label":"ACC00000026","value":3},{"label":"ACC00000030","value":6},{"label":"ACC00000045","value":4},{"label":"ACC000000YY","value":5}]
Is it the limit of Laravel Nova when 2 good plugins doesn't integrate with each other ? Anything I could do about it ?
Is it possible to hide certain widgets based or if they have authorization. I have tried using the canSee() function but it hides the entire view not just the widget. Any help would be appreciated.
Note: While possible to mix Static and Dynamic Widgets the static ones gets shifted around due to the lack of a setting to lock an item on the grid, this will likely be fixed once the original author of the underlying grid library accepts the suggestion of developing such feature: bensladden/vue-responsive-dash#196
This was completed Aug..6th I believe. Just curious if you have had a chance to test / implement. Thanks!
When I am on a mobile browser like chrome I am unable to get the navigation menu to slide out to navigate the site. It only happens on the page where I have the package deployed.
The package seems too great. But I have a problem with the nova version. I have nova 2.11.0 and composer say:
optimistdigital/nova-translatable 1.5.0 requires laravel/nova ^2.9 || ^3.0 -> satisfiable by laravel/nova[2.x-dev].
Which version do I need to use your package?
Scenario:
A sales team has a shared team-dashboard, which is shown on a smart-TV screen using a built-in browser.
This would require:
::Composer\Autoload\includeFile
vendor/digital-creative/nova-dashboard/src/NovaDashboard.php:13
It'd be nice if user is able to setup a dashboard and save it so other users can see it (or re-use it as a base).
Hi,
I've just released a date filter. It is a mix between pill filter and dateRangeFilter :)
Check it out. This filter is meant to limit date range that user would need when querying a dashboard with charts.
It can only be used with dcasia/nova-dashboard.
For my Nova menu, I have a section called Reports with two dashboards inside of it
MenuSection::make('Reports', [
MenuItem::dashboard(Demographics::class),
MenuItem::dashboard(Statistics::class),
])->icon('document-report'),
Anytime I click from one of those dashboards links to any other item in the menu, the menu state doesn't change. This is limited to just these two dashboards... If I remove the NovaDashboard
card from each dashboard, the issue resolves itself, so I figured it something to do with NovaDashboard
,
Ex dashboard
class Demographics extends Dashboard
{
public function name()
{
return 'Demographics';
}
public function cards()
{
return [
NovaDashboard::make()
->addView('Demographics Filters', function (View $view) {
$view->static();
return $view
->icon('window')
->addWidgets([
GenderWidget::make()->layout(4, 2, 9, 0),
])
->addFilters([
Gender::make(),
Status::make(),
Year::make(),
]);
}),
];
}
public function uriKey()
{
return 'demographics';
}
}
Demo
It doesn't show in the demo, but the new page loads with no issues after each click
Hi,
Right now, I have 6 value Widget on my dashboard, each one is making the same query, but select a different field or agregator.
It would be nice to have access to $filters
value in widgets()
function so that I can only make one query select all field, and then pass the value in the constructor of each widget.
Right now, I have to execute the same query 6 times
Here is an example of the value metrics I display
// DB::raw('sum(delta) as sum_delta'),
// DB::raw('avg(delta) as avg_delta'),
// DB::raw('sum(redistributed) as sum_redistr'),
// DB::raw('avg(redistributed) as avg_redistr'),
// DB::raw('avg(repartition_rate) as avg_repartition_rate'),
// DB::raw('sum(redistributed) / sum(delta) * 100) as autoprod_rate'),
It could be useful for simple widgets like ValueWidget
Besides, it could also allow conditional display of metrics depending on filters.
In my case, I have operations and meters (a meter belongs to an operation). Metrics are differents for a whole operation, or for a single meter.
as listed on https://vue-responsive-dash.netlify.app/api/#props-3 there are many options to control the behaviour of widgets.
Would be great to get control of these settings through the widgets options.
control if widgets can be resized and moved:
control of the dimensions of the widget (some widget will require fixed dimensions, others are flexible)
width
maxWidth
minWidth
height
maxHeight
minHeight
looks like
resources/js/components/Grid.vue
needs to be enhanced and the properties of course.
filter settings are currently to added to the url, so we can copy a direct link to a specific setup.
Would be great if the URL is updated with the filter data
Hi,
When adding a widget in static mode, with 5 params, it says it wants 0 or 4 :
if ($argumentsCount !== 0 && $argumentsCount !== 4) {
throw new RuntimeException('Invalid number of arguments, expected: { int $x, int $y, int $width, int $height } or none.');
}
so static mode is not working.
How to pass options to a widget ???
In combination with
https://github.com/gregoriohc/laravel-nova-theme-responsive
the dashboard does not behave responsive on smaller screens
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.