Specifying default extensions in angular2 and ng2-table - angularjs

I am trying to use ng2-table in my angular2 project. I installed angular2 and ng2-table using npm, when I run my app I get this 404 errors.
But the files exist in my project!
the server is looking for files with no extension, is there any way to specify default extension?
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms'; // <-- NgModel lives here
import {Ng2TableModule} from "ng2-table";
import { JobsComponent } from './components/jobs/jobs.component';
import { JobsTable } from './components/jobs/jobs.component';
#NgModule({
imports: [
BrowserModule,
FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
Ng2TableModule
],
declarations: [
JobsComponent,
JobsTable
],
bootstrap: [ JobsComponent,JobsTable ]
})
export class AppModule { }
jobs.components.ts
import {Component, OnInit} from '#angular/core';
#Component({
selector: 'jobs',
templateUrl: "./jobs.html"
})
export class JobsComponent {
}
export class JobsTable implements OnInit{
public rows:Array<any> = [];
public columns:Array<any> = [
{title: 'Name', name: 'name', filtering: {filterString: '', placeholder: 'Filter by name'}},
{
title: 'Position',
name: 'position',
sort: false,
filtering: {filterString: '', placeholder: 'Filter by position'}
},
{title: 'Office', className: ['office-header', 'text-success'], name: 'office', sort: 'asc'},
{title: 'Extn.', name: 'ext', sort: '', filtering: {filterString: '', placeholder: 'Filter by extn.'}},
{title: 'Start date', className: 'text-warning', name: 'startDate'},
{title: 'Salary ($)', name: 'salary'}
];
public page:number = 1;
public itemsPerPage:number = 10;
public maxSize:number = 5;
public numPages:number = 1;
public length:number = 0;
public config:any = {
paging: true,
sorting: {columns: this.columns},
filtering: {filterString: ''},
className: ['table-striped', 'table-bordered']
};
private data:Array<any> = null;
public constructor() {
this.length = this.data.length;
}
public ngOnInit():void {
this.onChangeTable(this.config);
}
public changePage(page:any, data:Array<any> = this.data):Array<any> {
let start = (page.page - 1) * page.itemsPerPage;
let end = page.itemsPerPage > -1 ? (start + page.itemsPerPage) : data.length;
return data.slice(start, end);
}
public changeSort(data:any, config:any):any {
if (!config.sorting) {
return data;
}
let columns = this.config.sorting.columns || [];
let columnName:string = void 0;
let sort:string = void 0;
for (let i = 0; i < columns.length; i++) {
if (columns[i].sort !== '' && columns[i].sort !== false) {
columnName = columns[i].name;
sort = columns[i].sort;
}
}
if (!columnName) {
return data;
}
// simple sorting
return data.sort((previous:any, current:any) => {
if (previous[columnName] > current[columnName]) {
return sort === 'desc' ? -1 : 1;
} else if (previous[columnName] < current[columnName]) {
return sort === 'asc' ? -1 : 1;
}
return 0;
});
}
public changeFilter(data:any, config:any):any {
let filteredData:Array<any> = data;
this.columns.forEach((column:any) => {
if (column.filtering) {
filteredData = filteredData.filter((item:any) => {
return item[column.name].match(column.filtering.filterString);
});
}
});
if (!config.filtering) {
return filteredData;
}
if (config.filtering.columnName) {
return filteredData.filter((item:any) =>
item[config.filtering.columnName].match(this.config.filtering.filterString));
}
let tempArray:Array<any> = [];
filteredData.forEach((item:any) => {
let flag = false;
this.columns.forEach((column:any) => {
if (item[column.name].toString().match(this.config.filtering.filterString)) {
flag = true;
}
});
if (flag) {
tempArray.push(item);
}
});
filteredData = tempArray;
return filteredData;
}
public onChangeTable(config:any, page:any = {page: this.page, itemsPerPage: this.itemsPerPage}):any {
if (config.filtering) {
Object.assign(this.config.filtering, config.filtering);
}
if (config.sorting) {
Object.assign(this.config.sorting, config.sorting);
}
let filteredData = this.changeFilter(this.data, this.config);
let sortedData = this.changeSort(filteredData, this.config);
this.rows = page && config.paging ? this.changePage(page, sortedData) : sortedData;
this.length = sortedData.length;
}
public onCellClick(data: any): any {
console.log(data);
}
}

Fixed by adding some few lines to systemjs.config.js,
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'#angular/animations' : 'npm:#angular/animations/bundles/animations.umd.js',
'ng2-table' : 'npm:ng2-table/ng2-table.js',
'systemjs' : 'npm:systemjs/dist/systemjs.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
......
.....
// this specify default extension for ng2-table
'ng2-table': {
format: 'register', defaultExtension: 'js'
}
}
});
})(this);

Related

Refresh Datatable in for:each loop: Lightning Web Components

I am having trouble refreshing a Datatable in my Lightning Web Component after updating a record. I am calling an onclick action on a button within the row, and imperatively calling an Apex method to update that record. I then call the refreshApex() to update the data being fed into the Datatable.
However, after the refreshApex(), the tables within the for:each are not being refreshed with new data.
The records are properly modified and reflect the changes properly when refreshing the entire page.
Note: The Task object is not supported in LWC, and I cannot use the updateRecord() method to update these records.
HTML:
<template>
<template if:true="{taskCompWrapperList}">
<!--<lightning-layout multiple-rows="false" pull-to-boundary="small">-->
<template for:each="{taskCompWrapperList}" for:item="taskTemplate">
<lightning-layout-item
key="{taskTemplate.taskSectionOrder}"
size="3"
class="slds-p-around_x-small"
>
<!-- Start bear tile -->
<lightning-card title="{taskTemplate.taskSectionTitle}">
<div class="slds-m-around_medium">
<template if:true="{taskTemplate.taskList}">
<lightning-datatable
key-field="Id"
data="{taskTemplate.taskList}"
onrowaction="{handleRowAction}"
columns="{columns}"
onsave="{handleSave}"
draft-values="{draftValues}"
>
</lightning-datatable>
</template>
<template if:true="{contact.error}">
<!-- handle Apex error -->
</template>
</div>
</lightning-card>
<!-- End bear tile -->
</lightning-layout-item>
</template>
<!--</lightning-layout>-->
</template>
</template>
Javascript:
import { LightningElement, api, wire ,track} from 'lwc';
import getTaskCompWrappers from '#salesforce/apex/ENT_Task_Utility.getTaskComponentWrapper';
import updateTask from '#salesforce/apex/ENT_Task_Utility.updateTask';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { updateRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '#salesforce/apex';
const COLS = [
{
type: 'button',
label: 'Complete',
typeAttributes:
{
//iconName: 'action:preview',
label: 'Complete',
name: 'Complete',
title: 'Complete',
value: 'Complete',
variant: 'brand',
alternativeText: 'Complete'
}
},
{
type: 'button-icon',
label: 'Start',
typeAttributes:
{
iconName: 'action:approval',
//label: 'Complete',
name: 'Start',
title: 'Start',
value: 'Start',
variant: 'success',
alternativeText: 'Start',
}
},
{
type: "button",
typeAttributes:
{
label: 'View',
name: 'View',
title: 'View',
disabled: false,
value: 'view',
iconPosition: 'left'
}
},
{
type: "button",
typeAttributes:
{
label: 'Edit',
name: 'Edit',
title: 'Edit',
disabled: false,
value: 'edit',
iconPosition: 'left'
}
},
//{ label: 'Complete', fieldName: 'Task_Complete__c', editable: true },
{ label: 'Status', fieldName: 'Status', type: 'picklist', editable: true },
{ label: 'Completed', fieldName: 'Completed', type: 'boolean', editable: true },
{ label: 'Owner', fieldName: 'OwnerId', editable: true },
{ label: 'Subject', fieldName: 'Subject' },
{ label: 'Due Date', fieldName: 'ActivityDate', type: 'date' }
];
export default class ENT_Task_Utility_LWC extends LightningElement {
#api objApiName;
#api recordId;
#track testMessage = 'Test Failed :c';
#track error;
#track columns = COLS;
#track draftValues = [];
taskCompWrapperList;
#track error;
//#wire(getTasks, {recordId: '$recordId'}) taskList;`
#wire(getTaskCompWrappers, {recordId: '$recordId', objApiName: '$objApiName'})
taskCompWrapperListWire({ error, data }) {
if (data) {
this.taskCompWrapperList = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.taskCompWrapperList = undefined;
}
}
updateTaskValues (taskId, taskStatus) {
// eslint-disable-next-line no-console
console.log('updateTaskValues hit');
for(var counter = 0; counter < this.taskCompWrapperList.length; counter++) {
// eslint-disable-next-line no-console
console.log('taskWrapper: ' + this.taskCompWrapperList[counter]);
for(var counter2 = 0; counter2 < this.taskCompWrapperList[counter].taskList.length; counter2++) {
// eslint-disable-next-line no-console
console.log('task: ' + this.taskCompWrapperList[counter].taskList[counter2]);
if(this.taskCompWrapperList[counter].taskList[counter2].Id == taskId)
{
this.dispatchEvent(
new ShowToastEvent({
title: 'Task Id Found!',
message: this.taskCompWrapperList[counter].taskList[counter2].Id,
variant: 'success'
})
);
this.taskCompWrapperList[counter].taskList[counter2].Status = taskStatus;
}
}
}
}
handleRowAction(event) {
//TODO
}
}
Apex methods:
#AuraEnabled(cacheable=true)
global static List<Task> getTasks(String recordId)
{
return [SELECT Id, Subject, OwnerId FROM Task WHERE WhatId = :recordId];
}
#AuraEnabled(cacheable=true)
global static List<ENT_Task_Comp_Wrapper> getTaskComponentWrapper(String recordId, String objApiName)
{
List<Task_Template__c> taskTemplateList = [SELECT Id, Task_Component_Section_Order__c, Task_Component_Section_Title__c, (SELECT Id FROM Task_Template_Items__r)
FROM Task_Template__c
WHERE Active__c = true AND sObject__c = :objApiName ORDER BY Task_Component_Section_Order__c ASC];
List<Task> taskList = [SELECT Id, Task_Template_Item__c, OwnerId, Owner.Name, Subject, Description, Status, ActivityDate, Task_Complete__c FROM TasK WHERE WhatId = :recordId];
List<ENT_Task_Comp_Wrapper> taskCompWrapperList = new List<ENT_Task_Comp_Wrapper>();
for(Task_Template__c taskTemplate : taskTemplateList)
{
ENT_Task_Comp_Wrapper taskCompWrapper = new ENT_Task_Comp_Wrapper();
taskCompWrapper.taskSectionTitle = taskTemplate.Task_Component_Section_Title__c;
taskCompWrapper.taskSectionOrder = (Integer)taskTemplate.Task_Component_Section_Order__c;
taskCompWrapper.taskList = new List<Task>();
for(Task currentTask : taskList)
{
for(Task_Template_Item__c taskTemplateItem : taskTemplate.Task_Template_Items__r)
{
if(taskTemplateItem.Id == currentTask.Task_Template_Item__c)
{
taskCompWrapper.taskList.add(currentTask);
}
}
}
taskCompWrapperList.add(taskCompWrapper);
}
System.debug(taskCompWrapperList);
return taskCompWrapperList;
}
#AuraEnabled
global static void updateTask(String taskId, String newStatus)
{
System.debug(taskId);
Task taskToUpdate = new Task(Id = taskId, Status = newStatus);
update taskToUpdate;
//update taskToUpdate;
}
#AuraEnabled
global static void updateTask(String taskId, String newStatus)
{
System.debug(taskId);
Task taskToUpdate = new Task(Id = taskId, Status = newStatus);
update taskToUpdate;
//update taskToUpdate;
}
In your JS code you have imported refreshApex
by using this line import { refreshApex } from '#salesforce/apex';
but you didn't assigned to any wire method. Hence data is not refreshed
Please refer this documentation.
To refresh a wired method, pass the argument the wired method receives (which is the wired value) to refreshApex(). In this sample code, the wired method is taskCompWrapperListWire. Hold on to the value provisioned by the wire service and pass it to refreshApex().
#wire(getTaskCompWrappers, {recordId: '$recordId', objApiName: '$objApiName'})
taskCompWrapperListWire({ error, data }) {
if (data) {
this.taskCompWrapperList = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.taskCompWrapperList = undefined;
}
}
And then use refreshApex() as below:
refreshApex(this.taskCompWrapperListWire);
Update you code as below
updateTaskValues({
taskId: this.taskId,
taskStatus: this. taskStatus
})
.then(() => {
// your code logic
refreshApex(this.taskCompWrapperListWire);
})
.catch((error) => {
this.message = 'Error received: code' + error.errorCode + ', ' +
'message ' + error.body.message;
});
you probably need to wait for next release to have a correct way to handle such situation.
You are getting record through uiRecordApi and updating through Apex if I'm correct.
Then you would need to use getRecordNotifyChange() available in Winter 21 release.
Apart from the answer provided by Sudarshan, you should also define taskCompWrapperList as a reactive property to make it rerender when the property is updated.
#track taskCompWrapperList = [];

JSON re-grouping with for each method in AngularJs

I am new in angular and i want to re-group a JSON. Is it possible to do with angular.forEach() method?
[
{
"doc":{
"Title":"Main",
"name":"Ajith",
"Day":"03",
"count":3
}
},
{
"doc":{
"Title":"starters",
"name":"Saji",
"Day":"01",
"count":39
}
},
{
"doc":{
"Title":"desert",
"name":"Sajeeb",
"Day":"02",
"count":63
}
},
{
"doc":{
"Title":"Main",
"name":"Suith",
"Day":"03",
"count":3
}
},
{
"doc":{
"Title":"starters",
"name":"Manu",
"Day":"01",
"count":9
}
}
]
I want the output should be like following.
{
"order":[
{
"Day":"01",
"Title":"starters",
"items":[
{
"name":"Saji",
"count":39
},
{
"name":"Manu",
"count":9
}
]
},
{
"Day":"02",
"Title":"desert",
"items":[
{
"name":"Sajeeb",
"count":63
}
]
},
{
"Day":"03",
"Title":"Main",
"items":[
{
"name":"Ajith",
"count":3
},
{
"name":"Suith",
"count":3
}
]
}
]
}
To regroup with angular.forEach() method.
Please help. Thanks.
https://jsfiddle.net/9fwtm0a0/4/
var days = [];
arr.forEach(function(d) {
var day = parseInt(d.doc.Day);
var item = { name: d.doc.name, count: d.doc.count };
if (days[day])
days[day].items.push(item);
else
days[day] = { Day: '0' + day, Title: d.doc.Title, items: item]};
});
days.sort(function (x, y) {
return parseInt(x.Day) > parseInt(y.Day);
});
var eventsinOrder = { order: days };
You don't really need the Angular forEach but you can easily substitute it in:
var days = [];
angular.forEach(arr, function (val, key) {
var day = parseInt(val.doc.Day);
var item = { name: val.doc.name, count: val.doc.count };
if (days[day])
days[day].items.push(item);
else
days[day] = { Day: '0' + day, Title: val.doc.Title, items: [item]};
});
days.sort(function (x, y) {
return parseInt(x.Day) > parseInt(y.Day);
});
var eventsinOrder = { order: days };
Either way you go you will still need to make use of the sort function (or any equivalent) to perform the sorting while you're building this resultant object.

-ISSUE- AngularMeteor,Meteor or Angular

thanks for read
Comportamientos:
when the result verCandidatos.postulados only have one value (like rectangle in img) never print the first time, I need refresh or click again and print values why?
html client
my-app/imports/ui/components/vacantes/verCandidatos/verCandidatos.html
<div ng-repeat="postulado in verCandidatos.postulados">
{{postulado.candidato().nombre}}
{{postulado.candidato().apellidos}}
{{postulado.candidato().sexo}}
</div>
Next the images:
//////////// ISUE img1
//////////// ISUE img2
js in client
my-app/imports/ui\components/vacantes/verCandidatos/verCandidatos.js
imports ...
class VerCandidatos {
constructor($scope, $reactive, $stateParams) {
'ngInject';
$reactive(this).attach($scope);
this.vacanteId = $stateParams.vacanteId;
this.subscribe('vacantes.candidatosOseleccionados', ()=>
[
{vacanteId: this.vacanteId},
{estado: 1}
]
);
this.helpers({
postulados (){
return Postulaciones.find();
}
});
}
}
collection.js
my-app/imports/api/postulaciones/collection.js
imports...
export const Postulaciones = new Mongo.Collection('postulaciones');
Postulaciones.deny({...});
Postulaciones.helpers({
candidato(){
return Candidatos.findOne({_id: this.candidatoId});
}
});
publish.js:
my-app/imports/api/vacantes/server/publish.js
imports...
if (Meteor.isServer) {
Meteor.publishComposite('vacantes.candidatosOseleccionados', function (vacanteId, estado) {
const selector = {$and: [estado, vacanteId]};
return {
find: function () {
return Postulaciones.find(selector);
},
children: [
{
find: function (postulacion) {
return Candidatos.find({_id: postulacion.candidatoId}, {
fields: {
nombre: 1,
apellidos: 1,
sexo: 1,
}
});
}
}
]
};
});
}
Any ideas?
- Thanks,

How can we disable other checkboxes by clicking a checkbox from a list in Angular2

I have an list of checkboxes. In this I want to disable other checkboxes When I have checked any item from list. Now if I uncheck that item then now all checkboxes need to be enabled.
This is the plunker - http://plnkr.co/edit/5FykLiZBQtQb2b31D9kE?p=preview
On unchecking any checkbox all the rest checkboxes are still disabled. How can I do this?
This is app.ts file -
import {bootstrap} from 'angular2/platform/browser';
import {Component} from 'angular2/core'
#Component({
selector: 'my-app',
template: `
<h2>{{title}}</h2>
<label *ngFor="let cb of checkboxes">
{{cb.label}}<input [disabled]="cb.status" type="checkbox"
[(ngModel)]="cb.state" (click)="buttonState(cb.id)">
</label><br />
{{debug}}
`
})
class App {
title = "Angular 2 - enable button based on checkboxes";
checkboxes = [{label: 'one',id: '0', status: ''},{label: 'two', id: '1', status:''},{label: 'three', id: '2', status: ''}];
constructor() {
//this.buttonState();
console.log("constructor called"); }
buttonState(id) {
console.log( id + "Button State Called");
//return this.checkboxes[0].status;
if(this.checkboxes[id].state == true){
this.checkboxes[id].status = true;
this.checkboxes[(id+1)%3].status = false;
this.checkboxes[(id+2)%3].status = false;
console.log("True Block");
}
else (this.checkboxes[id].state == false) {
this.checkboxes[id].status = false;
this.checkboxes[(id+1)%3].status = true;
this.checkboxes[(id+2)%3].status = true;
console.log("False Block");
}
}
get debug() {
return JSON.stringify(this.checkboxes);
}
}
bootstrap(App, [])
.catch(err => console.error(err));
Use this buttonState function instead yours:
buttonState(id) {
this.checkboxes.forEach(function(checkbox) {
if (checkbox.id !== id) {
checkbox.status = !checkbox.status;
}
})
}
see plunker example: http://plnkr.co/edit/ASzUR2jawpbifvwBXNO9?p=preview
another solution..not a good one...but still you can try it if you find any problem
class App {
title = "Angular 2 - enable button based on checkboxes";
checkboxes = [{label: 'one',id: '0', status: ''},{label: 'two', id: '1', status:''},{label: 'three', id: '2', status: ''}];
constructor() {
console.log("constructor called");
}
buttonState(id) {
console.log( this.checkboxes[id].state + "Button State Called");
//return this.checkboxes[0].status;
if(this.checkboxes[id].state == true ){
this.checkboxes[id].status = false;
this.checkboxes[(id+1)%3].status = false;
this.checkboxes[(id+2)%3].status = false;
console.log("True Block");
}
else if(this.checkboxes[id].state == false || this.checkboxes[id].state == undefined) {
this.checkboxes[id].status = false;
this.checkboxes[(id+1)%3].status = true;
this.checkboxes[(id+2)%3].status = true;
console.log("False Block");
}
}
http://plnkr.co/edit/mAvSQbyP9oh84LWfpGIm?p=preview

Dynamic angular chart

I'm trying to create a dynamic chart from userTemplate object.
I'm using this directive angular-flot and I want create the dataset and options of directive dynamically.
Its work but I have this error
Error: [$rootScope:infdig] http://errors.angularjs.org/1.2.21/$rootScope/infdig?p0=10&p1=%5B%5B%22fn%3…ection%5C%22%3A%7B%5C%22color%5C%22%3A%5C%22%2354728c%5C%22%7D%7D%22%5D%5D
at Error (native)
at http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:6:450
at k.$get.k.$digest (http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:110:66)
at k.$get.k.$apply (http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:112:173)
at http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:122:253
at e (http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:37:440)
at http://mwm3-gui/asset/script/vendor/angular2.1/angular.min.js:41:120
HTML
<div ng-repeat="panel in row.panels" class="{{panel.columnClass}}" resizable id="{{panel.id}}" r-directions="['right']">
<flot dataset="getDataForChart(panel)" options="getOptionForChart(panel)" height="{{panel.graph.height}}"></flot>
</div>
CONTROLLER
$scope.userTemplate = [
{
blockId: 'blockUno',
title: 'Block title',
rows: [
{
rowId: 'rowUno',
title: 'Row Title 1',
panels: [
{
id: 'palel-report-1',
title: 'uno',
columnClass: 'col-md-4',
graph: {
height: 250,
type: "BAR",
countBy: "status"
}
},
{
id: 'palel-report-2',
title: 'due',
columnClass: 'col-md-4',
graph: {
height: 250,
type: "PIE",
countBy: "status"
}
},
{
id: 'palel-report-3',
title: 'tre',
columnClass: 'col-md-4',
graph: {
height: 250,
type: "BAR",
countBy: "status"
}
}
]
}
],
tables: []
}
];
$scope.getDataForChart = function(panel) {
var graphData = [];
var countBy = panel.graph.countBy;
var arr = $scope.reportingData;
for (var i = 0; i < arr.length; i++) {
var valueOfkey = arr[i][countBy];
graphData.push(valueOfkey);
}
var a = [], b = [], prev;
graphData.sort();
for (var i = 0; i < graphData.length; i++) {
if (graphData[i] !== prev) {
a.push(graphData[i]);
b.push(1);
} else {
b[b.length - 1]++;
}
prev = graphData[i];
}
var graphData = [];
for (var i = 0; i < a.length; i++) {
var singleO = {label: '' + a[i], data: [[i, b[i]]]};
graphData.push(singleO);
}
return graphData;
};
$scope.getOptionForChart = function(panel) {
var options = angular.copy($scope.defaultPlotOptions);
var typeGraph = panel.graph.type;
switch (typeGraph) {
case "BAR":
options.series.bars.show = true;
break;
case "LINE":
options.series.lines.show = true;
break;
case "PIE":
options.series.pie.show = true;
break;
case "POINT":
options.series.points.show = true;
break;
case "TABLE":
break;
}
return options;
};
The error you get is from an infinite digest loop.
In a couple of places you are calling functions that return new items each time. Here's an example from the docs linked from the error message you received that suggests this may cause this error:
One common mistake is binding to a function which generates a new
array every time it is called. For example:
<div ng-repeat="user in getUsers()">{{ user.name }}</div>
$scope.getUsers = function() { return [ { name: 'Hank' }, { name: 'Francisco' } ]; };
Since getUsers() returns a new array, Angular
determines that the model is different on each $digest cycle,
resulting in the error. The solution is to return the same array
object if the elements have not changed:
var users = [ { name: 'Hank' }, { name: 'Francisco' } ];
$scope.getUsers = function() { return users; };
In your code, you are doing the same binding to getDataForChart and getOptionForChart.

Resources