Skip to content
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
53 changes: 53 additions & 0 deletions addon/components/inspection-form/details.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<div class="next-content-panel-wrapper" ...attributes>
<ContentPanel @title="Overview" @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container">
<div class="field-name">Name</div>
<div class="field-value">{{n-a @resource.name}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Status</div>
<div class="field-value"><Badge @status={{@resource.status}}>{{smart-humanize @resource.status}}</Badge></div>
</div>
<div class="field-info-container">
<div class="field-name">Type</div>
<div class="field-value">{{smart-humanize @resource.type}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Frequency</div>
<div class="field-value">{{smart-humanize @resource.frequency}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Items</div>
<div class="field-value">{{@resource.item_count}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Published</div>
<div class="field-value">{{n-a (format-date-fns @resource.published_at "dd MMM yyyy, HH:mm")}}</div>
</div>
<div class="field-info-container lg:col-span-2">
<div class="field-name">Description</div>
<div class="field-value whitespace-pre-wrap">{{n-a @resource.description}}</div>
</div>
</div>
</ContentPanel>

<ContentPanel @title="Checklist" @open={{true}} @wrapperClass="bordered-top">
<div class="space-y-2 text-xs">
{{#each @resource.items as |item|}}
<div class="flex items-center justify-between rounded border border-gray-200 dark:border-gray-700 p-2">
<div>
<div class="font-semibold">{{n-a item.label}}</div>
<div class="text-gray-500">{{n-a item.category}}</div>
</div>
<Badge @status={{if (eq item.severity "critical") "danger" "info"}}>{{smart-humanize item.severity}}</Badge>
</div>
{{else}}
<div class="text-gray-500">No checklist items configured.</div>
{{/each}}
</div>
</ContentPanel>

<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
<Spacer @height="200px" />
</div>
59 changes: 59 additions & 0 deletions addon/components/inspection-form/form.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<div class="inspection-form-form" ...attributes>
<ContentPanel @title="Form Details" @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2 no-input-group-padding text-xs">
<InputGroup @name="Name" @wrapperClass="lg:col-span-2">
<Input @value={{@resource.name}} @type="text" class="w-full form-input" placeholder="Daily vehicle inspection" disabled={{cannot-write @resource}} />
</InputGroup>
<InputGroup @name="Type">
<PowerSelect @options={{this.typeOptions}} @selected={{@resource.type}} @onChange={{fn (mut @resource.type)}} @triggerClass="form-select form-input" @disabled={{cannot-write @resource}} as |type|>
{{smart-humanize type}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Status">
<PowerSelect @options={{this.statusOptions}} @selected={{@resource.status}} @onChange={{fn (mut @resource.status)}} @triggerClass="form-select form-input" @disabled={{cannot-write @resource}} as |status|>
{{smart-humanize status}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Frequency">
<PowerSelect @options={{this.frequencyOptions}} @selected={{@resource.frequency}} @onChange={{fn (mut @resource.frequency)}} @triggerClass="form-select form-input" @disabled={{cannot-write @resource}} as |frequency|>
{{smart-humanize frequency}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Description" @wrapperClass="lg:col-span-2">
<Textarea @value={{@resource.description}} class="w-full form-input" rows={{3}} placeholder="What this inspection covers" disabled={{cannot-write @resource}} />
</InputGroup>
</div>
</ContentPanel>

<ContentPanel @title="Checklist Items" @open={{true}} @wrapperClass="bordered-top">
<div class="space-y-3">
{{#each this.items as |item index|}}
<div class="rounded border border-gray-200 dark:border-gray-700 p-3">
<div class="grid grid-cols-1 gap-2 lg:grid-cols-4 no-input-group-padding text-xs">
<InputGroup @name="Label" @wrapperClass="lg:col-span-2">
<Input @value={{item.label}} @type="text" class="w-full form-input" placeholder="Brake lights" {{on "input" (fn this.updateItem index "label" item.label)}} />
</InputGroup>
<InputGroup @name="Category">
<Input @value={{item.category}} @type="text" class="w-full form-input" placeholder="Lights" {{on "input" (fn this.updateItem index "category" item.category)}} />
</InputGroup>
<InputGroup @name="Severity">
<PowerSelect @options={{this.severityOptions}} @selected={{item.severity}} @onChange={{fn this.updateItem index "severity"}} @triggerClass="form-select form-input" as |severity|>
{{smart-humanize severity}}
</PowerSelect>
</InputGroup>
</div>
<div class="mt-2 flex justify-end">
<Button @text="Remove" @icon="trash" @size="xs" @type="danger" @onClick={{fn this.removeItem index}} />
</div>
</div>
{{/each}}
<Button @text="Add item" @icon="plus" @size="sm" @onClick={{this.addItem}} />
</div>
</ContentPanel>

<ContentPanel @title="Settings" @open={{false}} @wrapperClass="bordered-top">
<MetadataEditor @value={{@resource.settings}} @onChange={{fn (mut @resource.settings)}} />
</ContentPanel>

<CustomField::Yield @subject={{@resource}} @wrapperClass="bordered-top" />
</div>
58 changes: 58 additions & 0 deletions addon/components/inspection-form/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

const TYPE_OPTIONS = ['dvir', 'safety', 'compliance', 'maintenance', 'pre_trip', 'post_trip'];
const STATUS_OPTIONS = ['draft', 'published', 'archived'];
const FREQUENCY_OPTIONS = ['daily', 'weekly', 'monthly', 'pre_trip', 'post_trip', 'ad_hoc'];
const SEVERITY_OPTIONS = ['low', 'medium', 'high', 'critical'];

export default class InspectionFormFormComponent extends Component {
typeOptions = TYPE_OPTIONS;
statusOptions = STATUS_OPTIONS;
frequencyOptions = FREQUENCY_OPTIONS;
severityOptions = SEVERITY_OPTIONS;

@tracked items = [];

constructor(owner, args) {
super(owner, args);
this.items = [...(args.resource?.items ?? [])];
this.syncItems();
}

syncItems() {
this.args.resource.items = this.items;
}

@action addItem() {
this.items = [
...this.items,
{
key: `item_${this.items.length + 1}`,
label: '',
category: '',
required: true,
severity: 'medium',
},
];
this.syncItems();
}

@action removeItem(index) {
this.items = this.items.filter((_, itemIndex) => itemIndex !== index);
this.syncItems();
}

@action updateItem(index, key, value) {
this.items = this.items.map((item, itemIndex) => (itemIndex === index ? { ...item, [key]: value } : item));
this.syncItems();
}

@action setSetting(key, value) {
this.args.resource.settings = {
...(this.args.resource.settings ?? {}),
[key]: value,
};
}
}
86 changes: 86 additions & 0 deletions addon/components/inspection-submission/details.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<div class="next-content-panel-wrapper" ...attributes>
<ContentPanel @title="Overview" @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container">
<div class="field-name">Inspection</div>
<ClickToCopy @value={{@resource.public_id}} class="field-value font-mono">{{n-a @resource.public_id}}</ClickToCopy>
</div>
<div class="field-info-container">
<div class="field-name">Result</div>
<div class="field-value"><Badge @status={{if (eq @resource.result "failed") "danger" "success"}}>{{smart-humanize @resource.result}}</Badge></div>
</div>
<div class="field-info-container">
<div class="field-name">Form</div>
<div class="field-value">{{n-a (or @resource.form.name @resource.form_name)}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Status</div>
<div class="field-value"><Badge @status={{@resource.status}}>{{smart-humanize @resource.status}}</Badge></div>
</div>
<div class="field-info-container">
<div class="field-name">Vehicle</div>
<div class="field-value">{{n-a (or @resource.vehicle.displayName @resource.vehicle_name)}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Driver</div>
<div class="field-value">{{n-a (or @resource.driver.name @resource.driver_name)}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Odometer</div>
<div class="field-value">{{n-a @resource.odometer}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Engine Hours</div>
<div class="field-value">{{n-a @resource.engine_hours}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Submitted</div>
<div class="field-value">{{n-a (format-date-fns @resource.submitted_at "dd MMM yyyy, HH:mm")}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Resolved</div>
<div class="field-value">{{n-a (format-date-fns @resource.resolved_at "dd MMM yyyy, HH:mm")}}</div>
</div>
</div>
</ContentPanel>

<ContentPanel @title="Item Results" @open={{true}} @wrapperClass="bordered-top">
<div class="space-y-2 text-xs">
{{#each @resource.item_results as |item|}}
<div class="rounded border border-gray-200 dark:border-gray-700 p-3">
<div class="flex items-start justify-between gap-3">
<div>
<div class="font-semibold">{{n-a item.label}}</div>
<div class="text-gray-500">{{n-a item.category}}</div>
{{#if item.comments}}
<div class="mt-2 whitespace-pre-wrap">{{item.comments}}</div>
{{/if}}
</div>
<div class="flex items-center gap-2">
<Badge @status={{if item.passed "success" "danger"}}>{{if item.passed "Passed" "Failed"}}</Badge>
<Badge @status={{if (eq item.severity "critical") "danger" "info"}}>{{smart-humanize item.severity}}</Badge>
</div>
</div>
</div>
{{else}}
<div class="text-gray-500">No item results recorded.</div>
{{/each}}
</div>
</ContentPanel>

<ContentPanel @title="Follow Up" @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container">
<div class="field-name">Linked Issue</div>
<div class="field-value">{{n-a (or @resource.issue.public_id @resource.issue_uuid)}}</div>
</div>
<div class="field-info-container">
<div class="field-name">Linked Work Order</div>
<div class="field-value">{{n-a (or @resource.work_order.public_id @resource.work_order_uuid)}}</div>
</div>
</div>
</ContentPanel>

<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
<Spacer @height="200px" />
</div>
111 changes: 111 additions & 0 deletions addon/components/inspection-submission/form.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<div class="inspection-submission-form" ...attributes>
<ContentPanel @title="Inspection" @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2 no-input-group-padding text-xs">
<InputGroup @name="Form">
<ModelSelect
@modelName="inspection-form"
@selectedModel={{@resource.form}}
@query={{hash status="published"}}
@placeholder="Select inspection form"
@triggerClass="form-select form-input"
@infiniteScroll={{false}}
@renderInPlace={{true}}
@onChange={{this.assignForm}}
@disabled={{cannot-write @resource}}
as |form|
>
{{form.name}}
</ModelSelect>
</InputGroup>
<InputGroup @name="Status">
<PowerSelect @options={{this.statusOptions}} @selected={{@resource.status}} @onChange={{fn (mut @resource.status)}} @triggerClass="form-select form-input" @disabled={{cannot-write @resource}} as |status|>
{{smart-humanize status}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Vehicle">
<ModelSelect
@modelName="vehicle"
@selectedModel={{@resource.vehicle}}
@placeholder="Select vehicle"
@triggerClass="form-select form-input"
@infiniteScroll={{false}}
@renderInPlace={{true}}
@onChange={{this.assignVehicle}}
@disabled={{cannot-write @resource}}
as |vehicle|
>
{{or vehicle.displayName vehicle.name vehicle.public_id}}
</ModelSelect>
</InputGroup>
<InputGroup @name="Driver">
<ModelSelect
@modelName="driver"
@selectedModel={{@resource.driver}}
@placeholder="Select driver"
@triggerClass="form-select form-input"
@infiniteScroll={{false}}
@renderInPlace={{true}}
@onChange={{this.assignDriver}}
@disabled={{cannot-write @resource}}
as |driver|
>
{{or driver.name driver.public_id}}
</ModelSelect>
</InputGroup>
<InputGroup @name="Odometer">
<Input @value={{@resource.odometer}} @type="number" class="w-full form-input" placeholder="Current odometer" disabled={{cannot-write @resource}} />
</InputGroup>
<InputGroup @name="Engine Hours">
<Input @value={{@resource.engine_hours}} @type="number" class="w-full form-input" placeholder="Current engine hours" disabled={{cannot-write @resource}} />
</InputGroup>
</div>
</ContentPanel>

<ContentPanel @title="Checklist" @open={{true}} @wrapperClass="bordered-top">
<div class="space-y-3">
{{#each this.itemResults as |item index|}}
<div class="rounded border border-gray-200 dark:border-gray-700 p-3">
<div class="grid grid-cols-1 gap-2 lg:grid-cols-5 no-input-group-padding text-xs">
<InputGroup @name="Item" @wrapperClass="lg:col-span-2">
<Input @value={{item.label}} @type="text" class="w-full form-input" placeholder="Inspection item" {{on "input" (fn this.updateResult index "label" item.label)}} />
</InputGroup>
<InputGroup @name="Category">
<Input @value={{item.category}} @type="text" class="w-full form-input" placeholder="Category" {{on "input" (fn this.updateResult index "category" item.category)}} />
</InputGroup>
<InputGroup @name="Result">
<PowerSelect @options={{this.resultOptions}} @selected={{item.status}} @onChange={{fn this.updateResult index "status"}} @triggerClass="form-select form-input" as |result|>
{{smart-humanize result}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Severity">
<PowerSelect @options={{this.severityOptions}} @selected={{item.severity}} @onChange={{fn this.updateResult index "severity"}} @triggerClass="form-select form-input" as |severity|>
{{smart-humanize severity}}
</PowerSelect>
</InputGroup>
<InputGroup @name="Comments" @wrapperClass="lg:col-span-5">
<Textarea @value={{item.comments}} class="w-full form-input" rows={{2}} placeholder="Notes, defect description, or repair context" {{on "input" (fn this.updateResult index "comments" item.comments)}} />
</InputGroup>
</div>
<div class="mt-2 flex justify-end">
<Button @text="Remove" @icon="trash" @size="xs" @type="danger" @onClick={{fn this.removeResult index}} />
</div>
</div>
{{/each}}
<div class="flex items-center justify-between text-xs">
<div>
<span class="font-semibold">{{@resource.failed_items}}</span>
failed of
<span class="font-semibold">{{@resource.total_items}}</span>
items
</div>
<Button @text="Add result" @icon="plus" @size="sm" @onClick={{this.addResult}} />
</div>
</div>
</ContentPanel>

<ContentPanel @title="Metadata" @open={{false}} @wrapperClass="bordered-top">
<MetadataEditor @value={{@resource.meta}} @onChange={{fn (mut @resource.meta)}} />
</ContentPanel>

<CustomField::Yield @subject={{@resource}} @wrapperClass="bordered-top" />
</div>
Loading
Loading