diff --git a/app/Device.php b/app/Device.php index 931f6572df..eb367175fc 100644 --- a/app/Device.php +++ b/app/Device.php @@ -15,15 +15,38 @@ class Device extends Model implements Auditable use HasFactory; const REPAIR_STATUS_FIXED = 1; + const REPAIR_STATUS_FIXED_STR = 'Fixed'; const REPAIR_STATUS_REPAIRABLE = 2; + const REPAIR_STATUS_REPAIRABLE_STR = 'Repairable'; const REPAIR_STATUS_ENDOFLIFE = 3; + const REPAIR_STATUS_ENDOFLIFE_STR = 'End of life'; const SPARE_PARTS_NEEDED = 1; const SPARE_PARTS_NOT_NEEDED = 2; const SPARE_PARTS_UNKNOWN = 0; const PARTS_PROVIDER_MANUFACTURER = 1; + const PARTS_PROVIDER_MANUFACTURER_STR = 'Manufacturer'; const PARTS_PROVIDER_THIRD_PARTY = 2; + const PARTS_PROVIDER_THIRD_PARTY_STR = 'Third party'; + const PARTS_PROVIDER_NO = 0; + const PARTS_PROVIDER_NO_STR = 'No'; + + const BARRIER_SPARE_PARTS_NOT_AVAILABLE = 1; + const BARRIER_SPARE_PARTS_NOT_AVAILABLE_STR = 'Spare parts not available'; + const BARRIER_SPARE_PARTS_TOO_EXPENSIVE = 2; + const BARRIER_SPARE_PARTS_TOO_EXPENSIVE_STR = 'Spare parts too expensive'; + const BARRIER_NO_WAY_TO_OPEN_THE_PRODUCT = 3; + const BARRIER_NO_WAY_TO_OPEN_THE_PRODUCT_STR = 'No way to open the product'; + const BARRIER_REPAIR_INFORMATION_NOT_AVAILABLE = 4; + const BARRIER_REPAIR_INFORMATION_NOT_AVAILABLE_STR = 'Repair information not available'; + const BARRIER_LACK_OF_EQUIPMENT = 5; + const BARRIER_LACK_OF_EQUIPMENT_STR = 'Lack of equipment'; + + const NEXT_STEPS_MORE_TIME_NEEDED_STR = 'More time needed'; + const NEXT_STEPS_PROFESSIONAL_HELP_STR = 'Professional help'; + const NEXT_STEPS_DO_IT_YOURSELF_STR = 'Do it yourself'; + use \OwenIt\Auditing\Auditable; protected $table = 'devices'; @@ -33,7 +56,7 @@ class Device extends Model implements Auditable * * @var array */ - protected $fillable = ['event', 'category', 'category_creation', 'estimate', 'repair_status', 'spare_parts', 'parts_provider', 'brand', 'item_type', 'model', 'age', 'problem', 'notes', 'repaired_by', 'do_it_yourself', 'professional_help', 'more_time_needed', 'wiki', 'fault_type']; + protected $fillable = ['event', 'category', 'category_creation', 'estimate', 'repair_status', 'spare_parts', 'parts_provider', 'brand', 'item_type', 'model', 'age', 'problem', 'notes', 'repaired_by', 'do_it_yourself', 'professional_help', 'more_time_needed', 'wiki', 'fault_type', 'reference']; /** * The attributes that should be hidden for arrays. diff --git a/app/DeviceBarrier.php b/app/DeviceBarrier.php index 5b27f40518..4eaa637700 100644 --- a/app/DeviceBarrier.php +++ b/app/DeviceBarrier.php @@ -7,7 +7,7 @@ class DeviceBarrier extends Model { protected $table = 'devices_barriers'; - protected $fillable = ['device_id', 'barrier']; + protected $fillable = ['device_id', 'barrier_id']; protected $hidden = []; public $timestamps = false; } diff --git a/app/Http/Controllers/API/DeviceController.php b/app/Http/Controllers/API/DeviceController.php new file mode 100644 index 0000000000..c2d6ce1a74 --- /dev/null +++ b/app/Http/Controllers/API/DeviceController.php @@ -0,0 +1,629 @@ +getUser(); + + list($eventid, + $category, + $item_type, + $brand, + $model, + $age, + $estimate, + $problem, + $notes, + $repair_status, + $spare_parts, + $parts_provider, + $professional_help, + $more_time_needed, + $do_it_yourself, + $barrier, + $reference + ) = $this->validateDeviceParams($request,true); + + // We may have uploaded photos before creation, and we have a draft id which allows us to patch that + // up. + $draftId = $request->input('id'); + + $event = Party::findOrFail($eventid); + + if (!Fixometer::userHasEditEventsDevicesPermission($eventid, $user->id)) { + // Only hosts can add devices to events. + abort(403); + } + + $data = [ + 'event' => $eventid, + 'category' => $category, + 'category_creation' => $category, // We don't expose this over the API but we record it in case it changes. + 'item_type' => $item_type, + 'brand' => $brand, + 'model' => $model, + 'age' => $age, + 'estimate' => $estimate, + 'problem' => $problem, + 'notes' => $notes, + 'repair_status' => $repair_status, + 'spare_parts' => $spare_parts, + 'parts_provider' => $parts_provider, + 'professional_help' => $professional_help, + 'more_time_needed' => $more_time_needed, + 'do_it_yourself' => $do_it_yourself, + 'repaired_by' => $user->id, + 'reference' => $reference + ]; + + $device = Device::create($data); + $idDevice = $device->iddevices; + + if ($idDevice) { + event(new DeviceCreatedOrUpdated($device)); + + if ($barrier) { + DeviceBarrier::create([ + 'device_id' => $idDevice, + 'barrier_id' => $barrier + ]); + } + + // We might have some photos uploaded for this device. Record them against this device instance. + // Each instance of a device shares the same underlying photo file. + $File = new \FixometerFile; + $images = $File->findImages(env('TBL_DEVICES'), $draftId); + foreach ($images as $image) { + $xref = Xref::findOrFail($image->idxref); + $xref->copy($idDevice); + } + + // If the number of devices exceeds set amount then notify admins. + $deviceMiscCount = DB::table('devices')->where('category', env('MISC_CATEGORY_ID_POWERED'))->where('event', $eventid)->count() + + DB::table('devices')->where('category', env('MISC_CATEGORY_ID_UNPOWERED'))->where('event', $eventid)->count(); + if ($deviceMiscCount == env('DEVICE_ABNORMAL_MISC_COUNT', 5)) { + $notify_users = Fixometer::usersWhoHavePreference('admin-abnormal-devices'); + Notification::send($notify_users, new AdminAbnormalDevices([ + 'event_venue' => $event->getEventName(), + 'event_url' => url('/party/edit/'.$eventid), + ])); + } + } + + // We return the device and the stats to save the client another API call to update its store. + + return response()->json([ + 'id' => $idDevice, + 'device' => \App\Http\Resources\Device::make($device), + 'stats' => $event->getEventStats() + ]); + } + + /** + * @OA\Patch( + * path="/api/v2/devices/{id}", + * operationId="editDevice", + * tags={"Devices"}, + * summary="Edit Device", + * description="Edits a device.", + * @OA\Parameter( + * name="api_token", + * description="A valid user API token", + * required=true, + * in="query", + * @OA\Schema( + * type="string", + * example="1234" + * ) + * ), + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * required={"category","item_type"}, + * @OA\Property( + * property="eventid", + * title="id", + * description="Unique identifier of the event to which the device belongs", + * format="int64", + * example=1 + * ), + * @OA\Property( + * property="category", + * ref="#/components/schemas/Device/properties/category", + * ), + * @OA\Property( + * property="item_type", + * ref="#/components/schemas/Device/properties/item_type", + * ), + * @OA\Property( + * property="brand", + * ref="#/components/schemas/Device/properties/brand", + * ), + * @OA\Property( + * property="model", + * ref="#/components/schemas/Device/properties/model", + * ), + * @OA\Property( + * property="age", + * ref="#/components/schemas/Device/properties/age", + * ), + * @OA\Property( + * property="estimate", + * ref="#/components/schemas/Device/properties/estimate", + * ), + * @OA\Property( + * property="problem", + * ref="#/components/schemas/Device/properties/problem", + * ), + * @OA\Property( + * property="notes", + * ref="#/components/schemas/Device/properties/notes", + * ), + * @OA\Property( + * property="repair_status", + * ref="#/components/schemas/Device/properties/repair_status", + * ), + * @OA\Property( + * property="next_steps", + * ref="#/components/schemas/Device/properties/next_steps", + * ), + * @OA\Property( + * property="spare_parts", + * ref="#/components/schemas/Device/properties/spare_parts", + * ), + * @OA\Property( + * property="barrier", + * ref="#/components/schemas/Device/properties/barrier", + * ), + * @OA\Property( + * property="reference", + * ref="#/components/schemas/Device/properties/reference", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * title="data", + * ref="#/components/schemas/Device" + * ) + * ), + * ) + * ) + */ + public function updateDevicev2(Request $request, $iddevices) + { + $user = $this->getUser(); + + list($eventid, + $category, + $item_type, + $brand, + $model, + $age, + $estimate, + $problem, + $notes, + $repair_status, + $spare_parts, + $parts_provider, + $professional_help, + $more_time_needed, + $do_it_yourself, + $barrier, + $reference + ) = $this->validateDeviceParams($request,false); + + $event = Party::findOrFail($eventid); + + if (!Fixometer::userHasEditEventsDevicesPermission($eventid, $user->id)) { + // Only hosts can add devices to events. + abort(403); + } + + $data = [ + 'event' => $eventid, + 'category' => $category, + 'item_type' => $item_type, + 'brand' => $brand, + 'model' => $model, + 'age' => $age, + 'estimate' => $estimate, + 'problem' => $problem, + 'notes' => $notes, + 'repair_status' => $repair_status, + 'spare_parts' => $spare_parts, + 'parts_provider' => $parts_provider, + 'professional_help' => $professional_help, + 'more_time_needed' => $more_time_needed, + 'do_it_yourself' => $do_it_yourself, + 'repaired_by' => $user->id, + 'reference' => $reference + ]; + + $device = Device::findOrFail($iddevices); + $device->update($data); + + event(new DeviceCreatedOrUpdated($device)); + + if ($barrier) { + // We only allow one barrier now, so delete any old ones. + DeviceBarrier::where('device_id', $iddevices)->delete(); + DeviceBarrier::create([ + 'device_id' => $iddevices, + 'barrier_id' => $barrier + ]); + } + + return response()->json([ + 'id' => $iddevices, + 'device' => \App\Http\Resources\Device::make($device), + 'stats' => $event->getEventStats() + ]); + } + + /** + * @OA\Delete( + * path="/api/v2/devices/{id}", + * operationId="deleteDevice", + * tags={"Devices"}, + * summary="Delete Device", + * description="Deletes a device.", + * @OA\Parameter( + * name="id", + * description="Device id", + * required=true, + * in="path", + * @OA\Schema( + * type="integer" + * ) + * ), + * @OA\Response( + * response=200, + * description="Successful operation", + * ), + * @OA\Response( + * response=404, + * description="Device not found", + * ), + * ) + */ + + public function deleteDevicev2(Request $request, $iddevices) + { + $user = $this->getUser(); + + $device = Device::findOrFail($iddevices); + $eventid = $device->event; + + if (!Fixometer::userHasEditEventsDevicesPermission($device->event, $user->id)) { + // Only hosts can delete devices for events. + abort(403); + } + + $device->delete(); + + $event = Party::findOrFail($eventid); + + return response()->json([ + 'id' => $iddevices, + 'stats' => $event->getEventStats() + ]); + } + + private function validateDeviceParams(Request $request, $create): array + { + // We don't validate max lengths of other strings, to avoid duplicating the length information both here + // and in the migrations. If we wanted to do that we should extract the length dynamically from the + // schema, which is possible but not trivial. + $request->validate([ + 'eventid' => 'required|integer', + 'item_type' => 'string', // Some of the tests, at least, treat this as optional. + 'category' => 'required|integer', + 'brand' => 'string', + 'model' => 'string', + 'age' => [ 'numeric', 'max:500' ], + 'estimate' => [ 'numeric', 'min:0' ], + 'problem' => [ 'string', 'nullable' ], + 'notes' => 'string', + 'repair_status' => [ 'string', 'in:Fixed,Repairable,End of life' ], + 'next_steps' => [ 'string', 'in:More time needed,Professional help,Do it yourself', 'nullable' ], + 'spare_parts' => [ 'string', 'in:No,Manufacturer,Third party' ], + 'barrier' => [ 'string', 'nullable', 'in:Spare parts not available,Spare parts too expensive,No way to open the product,Repair information not available,Lack of equipment' ], + 'reference' => [ 'string', 'nullable', 'max:255' ] + ]); + + $eventid = $request->input('eventid'); + $category = $request->input('category'); + $item_type = $request->input('item_type'); + $brand = $request->input('brand'); + $model = $request->input('model'); + $age = $request->input('age'); + $age = $age ? $age : 0; + $estimate = $request->input('estimate'); + $estimate = $estimate ? $estimate : 0; + $problem = $request->input('problem'); + $notes = $request->input('notes'); + $repair_status = $request->input('repair_status'); + $barrierInput = $request->input('barrier'); + $reference = $request->input('reference'); + + // Our database has a slightly complex structure for historical reasons, so we need to map some input + // values to the underlying fields. This keeps the API clean. + // + // There is mirror code in Resources\Device. + $problem = $problem ? $problem : ''; + $spare_parts = Device::SPARE_PARTS_UNKNOWN; + $parts_provider = NULL; + $professional_help = 0; + $more_time_needed = 0; + $do_it_yourself = 0; + $barrier = 0; + + switch ($repair_status) { + case Device::REPAIR_STATUS_FIXED_STR: + $repair_status = Device::REPAIR_STATUS_FIXED; + break; + case Device::REPAIR_STATUS_REPAIRABLE_STR: + $repair_status = Device::REPAIR_STATUS_REPAIRABLE; + break; + case Device::REPAIR_STATUS_ENDOFLIFE_STR: + $repair_status = Device::REPAIR_STATUS_ENDOFLIFE; + break; + default: + $repair_status = 0; + break; + } + + if ($barrierInput) { + // Look up the barrier. + $barrierEnt = Barrier::firstOrFail()->where('barrier', $barrierInput)->get(); + $barrier = $barrierEnt->toArray()[0]['id']; + + if (!$barrier) { + throw ValidationException::withMessages(['barrier' => ['Invalid barrier supplied']]); + } + } + + // We can provide next_steps and spare_parts for any status - this is for recording historical information. + if ($request->has('next_steps')) { + switch ($request->input('next_steps')) { + case Device::NEXT_STEPS_MORE_TIME_NEEDED_STR: + $more_time_needed = 1; + break; + case Device::NEXT_STEPS_PROFESSIONAL_HELP_STR: + $professional_help = 1; + break; + case Device::NEXT_STEPS_DO_IT_YOURSELF_STR: + $do_it_yourself = 1; + break; + } + } + + if ($request->has('spare_parts')) { + switch ($request->input('spare_parts')) { + case Device::PARTS_PROVIDER_NO_STR: + $spare_parts = Device::SPARE_PARTS_NOT_NEEDED; + break; + case Device::PARTS_PROVIDER_MANUFACTURER_STR: + $spare_parts = Device::SPARE_PARTS_NEEDED; + + if ($repair_status != Device::REPAIR_STATUS_ENDOFLIFE) { + // If it is end of life we record that the parts are needed, but there is no provider. + $parts_provider = Device::PARTS_PROVIDER_MANUFACTURER; + } + break; + case Device::PARTS_PROVIDER_THIRD_PARTY_STR: + $spare_parts = Device::SPARE_PARTS_NEEDED; + + if ($repair_status != Device::REPAIR_STATUS_ENDOFLIFE) { + // If it is end of life we record that the parts are needed, but there is no provider. + $parts_provider = Device::PARTS_PROVIDER_THIRD_PARTY; + } + break; + } + } + + return [ + $eventid, + $category, + $item_type, + $brand, + $model, + $age, + $estimate, + $problem, + $notes, + $repair_status, + $spare_parts, + $parts_provider, + $professional_help, + $more_time_needed, + $do_it_yourself, + $barrier, + $reference + ]; + } + + private function getUser() + { + // We want to allow this call to work if a) we are logged in as a user, or b) we have a valid API token. + // + // This is a slightly odd thing to do, but it is necessary to get both the PHPUnit tests and the + // real client use of the API to work. + $user = Auth::user(); + + if (!$user) { + $user = auth('api')->user(); + } + + if (!$user) { + throw new AuthenticationException(); + } + + return $user; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/API/ItemController.php b/app/Http/Controllers/API/ItemController.php index a20aca4c7f..db477afe3c 100644 --- a/app/Http/Controllers/API/ItemController.php +++ b/app/Http/Controllers/API/ItemController.php @@ -20,7 +20,7 @@ class ItemController extends Controller * path="/api/v2/items", * operationId="listItemsv2", * tags={"Items"}, - * summary="Get list of items", + * summary="Get suggested list of items which could be used in a Device.", * @OA\Response( * response=200, * description="Successful operation", diff --git a/app/Http/Controllers/ApiController.php b/app/Http/Controllers/ApiController.php index cbfe6bcb08..277105e504 100644 --- a/app/Http/Controllers/ApiController.php +++ b/app/Http/Controllers/ApiController.php @@ -263,15 +263,15 @@ public static function getDevices(Request $request, $page, $size) ->take($size) ->get(); - foreach ($items as &$item) { - $item['shortProblem'] = $item->getShortProblem(); - $item['images'] = $item->getImages(); - $item['category'] = $item['deviceCategory']; + $item_data = []; + + foreach ($items as $item) { + $item_data[] = (new \App\Http\Resources\Device($item))->resolve(); } return response()->json([ 'count' => $count, - 'items' => $items, + 'items' => $item_data, ]); } diff --git a/app/Http/Controllers/DeviceController.php b/app/Http/Controllers/DeviceController.php index f231ff8b2a..ae8942b466 100644 --- a/app/Http/Controllers/DeviceController.php +++ b/app/Http/Controllers/DeviceController.php @@ -66,330 +66,6 @@ public function index($search = null) ]); } - public function ajaxCreate(Request $request) - { - $rules = [ - 'category' => 'required|filled' - ]; - - $validator = Validator::make($request->all(), $rules); - - if ($validator->fails()) { - return response()->json($validator->messages(), 200); - } - - $request->validate([ - 'age' => 'nullable|numeric|max:500' - ]); - - $category = $request->input('category'); - $weight = $request->filled('estimate') ? $request->input('estimate', 0) : 0; - $brand = $request->input('brand'); - $model = $request->input('model'); - $item_type = $request->input('item_type'); - $age = $request->filled('age') ? $request->input('age',0) : 0; - $problem = $request->input('problem'); - $notes = $request->input('notes'); - $repair_status = $request->input('repair_status'); - $repair_details = $request->input('repair_details'); - $spare_parts = $request->input('spare_parts'); - $quantity = $request->input('quantity'); - $event_id = $request->input('event_id'); - $barrier = $request->input('barrier'); - - $iddevices = $request->input('iddevices'); - - // Get party for later - $event = Party::find($event_id); - - // add quantity loop - for ($i = 0; $i < $quantity; $i++) { - $device[$i] = new Device; - $device[$i]->category = $category; - $device[$i]->category_creation = $category; - $device[$i]->estimate = $weight; - $device[$i]->brand = $brand; - $device[$i]->item_type = $item_type; - $device[$i]->model = $model; - $device[$i]->age = $age; - $device[$i]->problem = $problem; - $device[$i]->notes = $notes; - $device[$i]->repair_status = isset($repair_status) ? $repair_status : 0; - - if ($repair_details == 1) { - $device[$i]->more_time_needed = 1; - } else { - $device[$i]->more_time_needed = 0; - } - - if ($repair_details == 2) { - $device[$i]->professional_help = 1; - } else { - $device[$i]->professional_help = 0; - } - - if ($repair_details == 3) { - $device[$i]->do_it_yourself = 1; - } else { - $device[$i]->do_it_yourself = 0; - } - - // $spare_parts as input comes from a single dropdown with three options. - // This is mapped to corresponding DB values - // for spare_parts and parts_provider columns. - if ($spare_parts == 3) { // Input option was 'Third party' - $spare_parts = Device::SPARE_PARTS_NEEDED; - $parts_provider = Device::PARTS_PROVIDER_THIRD_PARTY; - } elseif ($spare_parts == 1) { // Input option was 'Manufacturer' - $spare_parts = Device::SPARE_PARTS_NEEDED; - $parts_provider = Device::PARTS_PROVIDER_MANUFACTURER; - } elseif ($spare_parts == 2) { // Input option was 'Not needed' - $spare_parts = Device::SPARE_PARTS_NOT_NEEDED; - $parts_provider = null; - } else { - $parts_provider = null; - } - - if (! isset($barrier)) { - $barrier = null; - } elseif (in_array(1, $barrier) || in_array(2, $barrier)) { // 'Spare parts not available' or 'spare parts too expensive' selected - $spare_parts = Device::SPARE_PARTS_NEEDED; - } elseif (count($barrier) > 0) { - $spare_parts = Device::SPARE_PARTS_NOT_NEEDED; - } - - $device[$i]->spare_parts = isset($spare_parts) ? $spare_parts : Device::SPARE_PARTS_UNKNOWN; - $device[$i]->parts_provider = $parts_provider; - $device[$i]->event = $event_id; - $device[$i]->repaired_by = Auth::id(); - - $device[$i]->save(); - $device[$i]->refresh(); - - event(new DeviceCreatedOrUpdated($device[$i])); - - // Update barriers - if (isset($barrier) && ! empty($barrier) && $repair_status == Device::REPAIR_STATUS_ENDOFLIFE) { - Device::find($device[$i]->iddevices)->barriers()->sync($barrier); - } else { - Device::find($device[$i]->iddevices)->barriers()->sync([]); - } - - // If the number of devices exceeds set amount then show the following message - $deviceMiscCount = DB::table('devices')->where('category', env('MISC_CATEGORY_ID_POWERED'))->where('event', $event_id)->count() + - DB::table('devices')->where('category', env('MISC_CATEGORY_ID_UNPOWERED'))->where('event', $event_id)->count(); - if ($deviceMiscCount == env('DEVICE_ABNORMAL_MISC_COUNT', 5)) { - $notify_users = Fixometer::usersWhoHavePreference('admin-abnormal-devices'); - Notification::send($notify_users, new AdminAbnormalDevices([ - 'event_venue' => $event->getEventName(), - 'event_url' => url('/party/edit/'.$event_id), - ])); - } - - // Expand a few things so that the new devices are returned with the same information that existing - // ones are returned in the view blade. - $device[$i]->idevents = $device[$i]->event; - $device[$i]->category = $device[$i]->deviceCategory; - $device[$i]->shortProblem = $device[$i]->getShortProblem(); - - $barriers = []; - - foreach ($device[$i]->barriers as $b) { - $barriers[] = $b->id; - } - - $device[$i]->barrier = $barriers; - - if ($iddevices && $iddevices < 0) { - // We might have some photos uploaded for this device. Record them against this device instance. - // Each instance of a device shares the same underlying photo file. - $File = new \FixometerFile; - $images = $File->findImages(env('TBL_DEVICES'), $iddevices); - foreach ($images as $image) { - $xref = Xref::findOrFail($image->idxref); - $xref->copy($device[$i]->iddevices); - } - - $device[$i]->images = $device[$i]->getImages(); - } - } - // end quantity loop - - $return['success'] = true; - $return['devices'] = $device; - - $return['stats'] = $event->getEventStats(); - - return response()->json($return); - } - - public function ajaxEdit(Request $request, $id) - { - $category = $request->input('category'); - $brand = $request->input('brand'); - $item_type = $request->input('item_type'); - $model = $request->input('model'); - $age = $request->filled('age') ? $request->input('age',0) : 0; - $problem = $request->input('problem'); - $notes = $request->input('notes'); - $repair_status = $request->input('repair_status'); - $barrier = $request->input('barrier'); - $repair_details = $request->input('repair_details'); - $spare_parts = $request->input('spare_parts'); - $event_id = $request->input('event_id'); - $wiki = $request->input('wiki'); - $estimate = $request->filled('estimate') ? $request->input('estimate', 0) : 0; - - $request->validate([ - 'age' => 'nullable|numeric|max:500' - ]); - - if (empty($repair_status)) { //Override - $repair_status = 0; - } - - if ($repair_status != 2) { //Override - $repair_details = 0; - } - - if (Fixometer::userHasEditEventsDevicesPermission($event_id)) { - if ($repair_details == 1) { - $more_time_needed = 1; - } else { - $more_time_needed = 0; - } - - if ($repair_details == 2) { - $professional_help = 1; - } else { - $professional_help = 0; - } - - if ($repair_details == 3) { - $do_it_yourself = 1; - } else { - $do_it_yourself = 0; - } - - if ($spare_parts == 3) { // Third party - $spare_parts = 1; - $parts_provider = 2; - } elseif ($spare_parts == 1) { // Manufacturer - $spare_parts = 1; - $parts_provider = 1; - } elseif ($spare_parts == 2) { // Not needed - $spare_parts = 2; - $parts_provider = null; - } elseif ($spare_parts == 4) { // Historical data, resets spare parts to 1 but keeps parts provider as null - $spare_parts = 1; - $parts_provider = null; - } else { - $parts_provider = null; - } - - if (! isset($barrier)) { - $barrier = null; - } elseif (in_array(1, $barrier) || in_array(2, $barrier)) { // 'Spare parts not available' or 'spare parts too expensive' selected - $spare_parts = 1; - } elseif (count($barrier) > 0) { - $spare_parts = 2; - } - - $Device = Device::find($id); - - $Device->update([ - 'category' => $category, - 'brand' => $brand, - 'item_type' => $item_type, - 'model' => $model, - 'age' => $age, - 'problem' => $problem, - 'notes' => $notes, - 'spare_parts' => $spare_parts, - 'parts_provider' => $parts_provider, - 'repair_status' => $repair_status, - 'more_time_needed' => $more_time_needed, - 'professional_help' => $professional_help, - 'do_it_yourself' => $do_it_yourself, - 'wiki' => $wiki, - 'estimate' => $estimate, - ]); - - // Update barriers - if (isset($barrier) && ! empty($barrier) && $repair_status == 3) { // Only sync when repair status is end-of-life - $Device->barriers()->sync($barrier); - } else { - $Device->barriers()->sync([]); - } - - $event = Party::find($event_id); - event(new DeviceCreatedOrUpdated($Device)); - - $stats = $event->getEventStats(); - $data['stats'] = $stats; - $data['success'] = 'Device updated!'; - - // Expand a few things so that the devices are returned with the same information that existing - // ones are returned in the view blade. - $device = Device::find($id); - $device->idevents = $device->event; - $device->category = $device->deviceCategory; - $device->shortProblem = $device->getShortProblem(); - $device->images = $device->getImages(); - - $barriers = []; - - foreach ($device->barriers as $b) { - $barriers[] = $b->id; - } - - $device->barrier = $barriers; - - $data['device'] = $device; - - return response()->json($data); - } - } - - public function delete(Request $request, $id) - { - $user = Auth::user(); - - $device = Device::find($id); - - if ($device) { - $eventId = $device->event; - $is_attending = EventsUsers::where('event', $device->event)->where('user', Auth::id())->first(); - $is_attending = is_object($is_attending) && $is_attending->status == 1; - - if (Fixometer::hasRole($user, 'Administrator') || - Fixometer::userHasEditPartyPermission($eventId, $user->id) || - $is_attending - ) { - $device->delete(); - - if ($request->ajax()) { - $event = Party::find($eventId); - $stats = $event->getEventStats(); - - return response()->json([ - 'success' => true, - 'stats' => $stats, - ]); - } - - return redirect('/party/view/'.$eventId)->with('success', __('devices.device_delete_sucess')); - } - } - - if ($request->ajax()) { - return response()->json(['success' => false]); - } - - \Sentry\CaptureMessage(__('devices.device_delete_permissions')); - return redirect('/party/view/'.$eventId)->with('warning', __('devices.device_delete_permissions')); - } - public function imageUpload(Request $request, $id) { try { @@ -401,7 +77,7 @@ public function imageUpload(Request $request, $id) if ($id > 0) { // We are adding a photo to an existing device. $fn = $file->upload('file', 'image', $id, env('TBL_DEVICES'), true, false, true); - $device = Device::find($id); + $device = Device::findOrFail($id); $images = $device->getImages(); } else { // We are adding a photo for a device that hasn't yet been added. Upload the file. We will add diff --git a/app/Http/Resources/Category.php b/app/Http/Resources/Category.php new file mode 100644 index 0000000000..fcd8d1ac13 --- /dev/null +++ b/app/Http/Resources/Category.php @@ -0,0 +1,52 @@ + $this->idcategories, + 'name' => $this->name, + 'powered' => $this->powered, + ]; + } +} diff --git a/app/Http/Resources/Device.php b/app/Http/Resources/Device.php new file mode 100644 index 0000000000..2f7d678324 --- /dev/null +++ b/app/Http/Resources/Device.php @@ -0,0 +1,281 @@ +event); + $group = $event ? \App\Group::find($event->group) : NULL; + + $ret = [ + 'id' => intval($this->iddevices), + 'eventid' => intval($this->event), + 'eventtitle' => $event ? $event->title : NULL, + 'groupid' => $event ? intval($event->group) : NULL, + 'groupname' => $group ? $group->name : NULL, + 'category' => intval($this->category), + 'item_type' => $this->item_type, + 'brand' => $this->brand, + 'model' => $this->model, + 'age' => floatval($this->age), + 'estimate' => floatval($this->estimate), + 'problem' => $this->problem, + 'short_problem' => $this->getShortProblem(), + 'notes' => $this->notes, + 'created_at' => $this->created_at->toIso8601String(), + 'updated_at' => $this->created_at->toIso8601String(), + 'reference' => $this->reference, + ]; + + // Our database has a slightly complex structure for historical reasons, so we need to map some underlying + // fields to simpler values to keep the API clean. + // + // There is mirror code in API\DeviceController. + switch ($this->repair_status) { + case \App\Device::REPAIR_STATUS_FIXED: + $ret['repair_status'] = \App\Device::REPAIR_STATUS_FIXED_STR; + break; + case \App\Device::REPAIR_STATUS_REPAIRABLE: + $ret['repair_status'] = \App\Device::REPAIR_STATUS_REPAIRABLE_STR; + break; + case \App\Device::REPAIR_STATUS_ENDOFLIFE: + $ret['repair_status'] = \App\Device::REPAIR_STATUS_ENDOFLIFE_STR; + break; + } + + if ($this->more_time_needed) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_MORE_TIME_NEEDED_STR; + } else if ($this->professional_help) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_PROFESSIONAL_HELP_STR; + } else if ($this->do_it_yourself) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_DO_IT_YOURSELF_STR; + } + + if ($this->spare_parts === \App\Device::SPARE_PARTS_NOT_NEEDED) { + $ret['spare_parts'] = \App\Device::PARTS_PROVIDER_NO_STR; + } else if ($this->spare_parts === null) { + $ret['spare_parts'] = null; + } else { + if ($this->parts_provider === \App\Device::PARTS_PROVIDER_MANUFACTURER) { + $ret['spare_parts'] = \App\Device::PARTS_PROVIDER_MANUFACTURER_STR; + } else if ($this->parts_provider === \App\Device::PARTS_PROVIDER_THIRD_PARTY) { + $ret['spare_parts'] = \App\Device::PARTS_PROVIDER_THIRD_PARTY_STR; + } + } + + if ($this->professional_help) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_PROFESSIONAL_HELP_STR; + } else if ($this->do_it_yourself) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_DO_IT_YOURSELF_STR; + } else if ($this->more_time_needed) { + $ret['next_steps'] = \App\Device::NEXT_STEPS_MORE_TIME_NEEDED_STR; + } else { + $ret['next_steps'] = null; + } + + // The underlying DB might have multiple barriers, but we only support one across the API. + foreach ($this->resource->barriers as $barrier) { + $ret['barrier'] = $barrier->barrier; + break; + } + + $category = \App\Category::find($this->category); + $ret['category']= \App\Http\Resources\Category::make($category); + + $images = $this->resource->getImages(); + $ret['images'] = \App\Http\Resources\Image::collection($images); + + return $ret; + } +} diff --git a/app/Http/Resources/Image.php b/app/Http/Resources/Image.php new file mode 100644 index 0000000000..436b508a52 --- /dev/null +++ b/app/Http/Resources/Image.php @@ -0,0 +1,45 @@ + $this->idimages, + 'idxref' => $this->idxref, + 'path' => $this->path + ]; + } +} diff --git a/app/Http/Resources/Item.php b/app/Http/Resources/Item.php index 2e5a80aae7..fd9a870d2b 100644 --- a/app/Http/Resources/Item.php +++ b/app/Http/Resources/Item.php @@ -8,7 +8,7 @@ * @OA\Schema( * title="Item", * schema="Item", - * description="An item which can be specified in a device", + * description="An item which can be specified in a Device", * @OA\Property( * property="type", * title="type", diff --git a/app/Party.php b/app/Party.php index cbe94b955e..cd174cdd54 100644 --- a/app/Party.php +++ b/app/Party.php @@ -47,7 +47,7 @@ class Party extends Model implements Auditable 'user_id', 'network_data', ]; - protected $hidden = ['created_at', 'deleted_at', 'frequency', 'group', 'group', 'user_id', 'wordpress_post_id', 'cancelled', 'devices_updated_at']; + protected $hidden = ['created_at', 'deleted_at', 'frequency', 'group', 'user_id', 'wordpress_post_id', 'cancelled', 'devices_updated_at']; // Eager-loading the group reduces N+1 queries. protected $with = 'theGroup'; diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 159764bcc8..adb0175cd4 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -69,7 +69,7 @@ public function boot() protected function configureRateLimiting() { RateLimiter::for('api', function (Request $request) { - return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + return Limit::perMinute(300)->by($request->user()?->id ?: $request->ip()); }); } } diff --git a/database/migrations/2025_01_07_152513_device_reference.php b/database/migrations/2025_01_07_152513_device_reference.php new file mode 100644 index 0000000000..1019dff1b8 --- /dev/null +++ b/database/migrations/2025_01_07_152513_device_reference.php @@ -0,0 +1,32 @@ +string('reference')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('devices', function (Blueprint $table) { + $table->dropColumn('reference'); + }); + } +}; diff --git a/lang/de/partials.php b/lang/de/partials.php index 2bad1a26c3..b8e9d53625 100644 --- a/lang/de/partials.php +++ b/lang/de/partials.php @@ -51,11 +51,9 @@ 'least_repaired' => 'Least repaired', 'most_repaired' => 'Most repaired', 'most_seen' => 'Most seen', - 'solution_text' => 'Could the solution comments help Restarters working on a similar device in future? Or is it a fun case study?', 'no_devices_added' => 'No devices added', 'add_a_device' => 'Add a device', 'event_requires_moderation_by_an_admin' => 'Event requires moderation by an admin', 'save' => 'Save item', - 'solution_text2' => 'Interesting case study to share?', 'cancel' => 'Cancel', ]; diff --git a/lang/en/devices.php b/lang/en/devices.php index a0738d7f20..5410cddd46 100644 --- a/lang/en/devices.php +++ b/lang/en/devices.php @@ -66,7 +66,6 @@ 'assessment' => 'Assessment', 'item_and_repair_info' => 'Item & Repair Info', 'search_assessment_comments' => 'Assessment', - 'interesting_case_study' => 'Interesting case study', 'tooltip_type' => 'Add as much information about the type of item here as you can (e.g. `Denim jeans` or ‘Sofa’)', 'add_data_group' => 'Please select a group', 'add_data_event' => 'Please select an event', @@ -79,6 +78,6 @@ 'image_delete_success' => 'Thank you, the image has been deleted', 'image_delete_error' => 'Sorry, but the image can\'t be deleted', 'image_upload_error' => 'fail - image could not be uploaded', - 'device_delete_sucess' => 'Device has been deleted!', - 'device_delete_permissions' => 'You do not have the right permissions for deleting a device.', + 'reference' => 'Reference', + 'tooltip_reference' => 'You can optionally record your own reference ID for this item, to cross-reference it to other software tools you are using.', ]; diff --git a/lang/en/partials.php b/lang/en/partials.php index 1f887ebcd7..b0d07ba9f8 100644 --- a/lang/en/partials.php +++ b/lang/en/partials.php @@ -52,7 +52,6 @@ 'least_repaired' => 'Least repaired', 'most_repaired' => 'Most repaired', 'most_seen' => 'Most seen', - 'solution_text' => 'Could the solution comments help volunteers working on a similar device in future? Or is it a fun case study?', 'no_devices_added' => 'No devices added', 'add_a_device' => 'Add a device', 'event_requires_moderation_by_an_admin' => 'Event requires moderation by an admin', @@ -60,10 +59,9 @@ 'hot_topics' => 'Hot topics', 'hot_topics_text' => 'Join in on the latest hot topics on repair at Restarters Talk.', 'hot_topics_link' => 'All weekly hot topics', - 'choose_barriers' => 'Choose barriers to repair', + 'choose_barriers' => 'Choose main barrier to repair', 'add_device_powered' => 'Add Powered Item', 'add_device_unpowered' => 'Add Unpowered Item', - 'solution_text2' => 'Interesting case study to share?', 'emissions_equivalent_consume_low' => "that's like growing :value tree seedling for 10 years|that's like growing :value tree seedlings for 10 years", 'emissions_equivalent_consume_high' => "that's like planting around :value hectare of trees|that's like planting around :value hectares of trees", 'emissions_equivalent_consume_low_explanation' => 'Medium growth coniferous or deciduous trees, planted in an urban setting and grown for 10 years.', diff --git a/lang/fr-BE/devices.php b/lang/fr-BE/devices.php index 5798fdc0fa..286c833b31 100644 --- a/lang/fr-BE/devices.php +++ b/lang/fr-BE/devices.php @@ -63,7 +63,6 @@ 'status' => 'Statut', 'latest_data' => 'Dernières données', 'assessment' => 'Evaluation', - 'interesting_case_study' => 'Cas d\'étude intéressant', 'item_and_repair_info' => 'Appareils et informations de réparation', 'search_assessment_comments' => 'Evaluation', 'table_intro' => 'Cliquez sur la ligne pour plus de détails. Cliquez sur l\'en-tête d\'une colonne pour trier par cette colonne - cliquez à nouveau pour inverser l\'ordre de tri.', @@ -79,6 +78,6 @@ 'image_delete_success' => 'Merci, l\'image a été supprimée', 'image_delete_error' => 'Désolé, mais l\'image ne peut pas être supprimée.', 'image_upload_error' => 'fail - l\'image n\'a pas pu être téléchargée', - 'device_delete_sucess' => 'L\'appareil a été supprimé!', - 'device_delete_permissions' => 'Vous n\'avez pas les autorisations nécessaires pour supprimer un appareil.', + 'reference' => 'Référence', + 'tooltip_reference' => 'Vous pouvez éventuellement enregistrer votre propre identifiant de référence pour cet élément, afin de le recouper avec d\'autres outils logiciels que vous utilisez.', ]; diff --git a/lang/fr-BE/partials.php b/lang/fr-BE/partials.php index 581e4afcb8..f621ec3351 100644 --- a/lang/fr-BE/partials.php +++ b/lang/fr-BE/partials.php @@ -52,7 +52,6 @@ 'least_repaired' => 'Dernier réparé', 'most_repaired' => 'Les plus réparés', 'most_seen' => 'Les plus vus', - 'solution_text' => 'Est-ce que la solution en commentaire aide un réparateur à travailler sur un cas similiaire? Ou est-ce une étude de cas amusante?', 'no_devices_added' => 'Pas d\'appareils ajoutés', 'add_a_device' => 'Ajouter un appareil', 'event_requires_moderation_by_an_admin' => 'L\'événement nécessite l\'approbation d\'un administrateur', @@ -60,8 +59,7 @@ 'hot_topics' => 'Sujets d\'actualité', 'hot_topics_link' => 'Tous les derniers sujets d\'actualités', 'hot_topics_text' => 'Prenez part au dernier sujet d\'actualité de réparation sur Restarters Talk', - 'choose_barriers' => 'Choisir les freins à la réparation', - 'solution_text2' => 'Cas d\'étude intéressant à partager?', + 'choose_barriers' => 'Choisir l\'obstacle principal à la réparation', 'quantity' => 'Quantité', 'cancel' => 'Annuler', 'powered_only' => '(calculé pour les objets électriques uniquement)', diff --git a/lang/fr/devices.php b/lang/fr/devices.php index 5798fdc0fa..8eb95e04b6 100644 --- a/lang/fr/devices.php +++ b/lang/fr/devices.php @@ -63,7 +63,6 @@ 'status' => 'Statut', 'latest_data' => 'Dernières données', 'assessment' => 'Evaluation', - 'interesting_case_study' => 'Cas d\'étude intéressant', 'item_and_repair_info' => 'Appareils et informations de réparation', 'search_assessment_comments' => 'Evaluation', 'table_intro' => 'Cliquez sur la ligne pour plus de détails. Cliquez sur l\'en-tête d\'une colonne pour trier par cette colonne - cliquez à nouveau pour inverser l\'ordre de tri.', @@ -79,6 +78,6 @@ 'image_delete_success' => 'Merci, l\'image a été supprimée', 'image_delete_error' => 'Désolé, mais l\'image ne peut pas être supprimée.', 'image_upload_error' => 'fail - l\'image n\'a pas pu être téléchargée', - 'device_delete_sucess' => 'L\'appareil a été supprimé!', - 'device_delete_permissions' => 'Vous n\'avez pas les autorisations nécessaires pour supprimer un appareil.', + 'reference' => 'Référence', + 'tooltip_reference' => 'Vous pouvez éventuellement enregistrer votre propre identifiant de référence pour cet élément, afin de le recouper avec d\'autres outils logiciels que vous utilisez.', ]; diff --git a/lang/fr/partials.php b/lang/fr/partials.php index 581e4afcb8..f621ec3351 100644 --- a/lang/fr/partials.php +++ b/lang/fr/partials.php @@ -52,7 +52,6 @@ 'least_repaired' => 'Dernier réparé', 'most_repaired' => 'Les plus réparés', 'most_seen' => 'Les plus vus', - 'solution_text' => 'Est-ce que la solution en commentaire aide un réparateur à travailler sur un cas similiaire? Ou est-ce une étude de cas amusante?', 'no_devices_added' => 'Pas d\'appareils ajoutés', 'add_a_device' => 'Ajouter un appareil', 'event_requires_moderation_by_an_admin' => 'L\'événement nécessite l\'approbation d\'un administrateur', @@ -60,8 +59,7 @@ 'hot_topics' => 'Sujets d\'actualité', 'hot_topics_link' => 'Tous les derniers sujets d\'actualités', 'hot_topics_text' => 'Prenez part au dernier sujet d\'actualité de réparation sur Restarters Talk', - 'choose_barriers' => 'Choisir les freins à la réparation', - 'solution_text2' => 'Cas d\'étude intéressant à partager?', + 'choose_barriers' => 'Choisir l\'obstacle principal à la réparation', 'quantity' => 'Quantité', 'cancel' => 'Annuler', 'powered_only' => '(calculé pour les objets électriques uniquement)', diff --git a/lang/it/partials.php b/lang/it/partials.php index fea5cdc7b2..8058388cb6 100644 --- a/lang/it/partials.php +++ b/lang/it/partials.php @@ -51,7 +51,6 @@ 'least_repaired' => 'Meno riparato', 'most_repaired' => 'Piu\' riparato', 'most_seen' => 'Piu\' visto', - 'solution_text' => 'I commenti della soluzione potrebbero aiutare i Restarters a lavorare su un dispositivo simile in futuro? O è un qualcosa di divertente?', 'no_devices_added' => 'Nessun dispositivo aggiunto', 'add_a_device' => 'Aggiungi un dispositivo', 'event_requires_moderation_by_an_admin' => 'L\'evento richiede la moderazione di un @@ -60,6 +59,5 @@ 'hot_topics' => 'Argomenti piu\' seguiti', 'hot_topics_link' => 'Argomenti piu\' seguiti della settimana', 'hot_topics_text' => 'Unisciti alla conversazione su gli argomenti piu\' seguiti della settimana', - 'solution_text2' => 'Interesting case study to share?', 'cancel' => 'Cancel', ]; diff --git a/lang/nl-BE/partials.php b/lang/nl-BE/partials.php index c74258a492..4e76f1ee78 100644 --- a/lang/nl-BE/partials.php +++ b/lang/nl-BE/partials.php @@ -46,7 +46,6 @@ 'save' => 'Toestel opslaan', 'see_all_events' => 'Alle activiteiten bekijken', 'see_more_posts' => 'Meer posts bekijken', - 'solution_text' => 'Is je oplossing nuttig voor andere Restarters die een gelijkaardig toestel proberen te herstellen? Of is het een leuke case study?', 'update' => 'Updaten', 'view_the_materials' => 'Tips en info bekijken', 'welcome_text' => 'We geven info en tips over het organiseren van repareer activiteiten en helpen je om je competenties te delen.', @@ -58,6 +57,5 @@ 'yes_third_party' => 'Ja - van een derde partij', 'your_recent_events' => 'Je meest recente activiteiten', 'your_recent_events_txt1' => 'Dit zijn activiteiten waarop je antwoordde of waaraan je werd toegevoegd door een organisator', - 'solution_text2' => 'Interesting case study to share?', 'cancel' => 'Cancel', ]; diff --git a/lang/no/partials.php b/lang/no/partials.php index 28f0fef04a..39cc83be4b 100644 --- a/lang/no/partials.php +++ b/lang/no/partials.php @@ -51,11 +51,9 @@ 'least_repaired' => 'Least repaired', 'most_repaired' => 'Most repaired', 'most_seen' => 'Most seen', - 'solution_text' => 'Could the solution comments help Restarters working on a similar device in future? Or is it a fun case study?', 'no_devices_added' => 'No devices added', 'add_a_device' => 'Add a device', 'event_requires_moderation_by_an_admin' => 'Event requires moderation by an admin', 'save' => 'Save item', - 'solution_text2' => 'Interesting case study to share?', 'cancel' => 'Cancel', ]; diff --git a/resources/js/components/DeviceAge.vue b/resources/js/components/DeviceAge.vue index 4bffe7035b..90aceebe61 100644 --- a/resources/js/components/DeviceAge.vue +++ b/resources/js/components/DeviceAge.vue @@ -3,7 +3,7 @@ - + {{ __('devices.age_approx') }} diff --git a/resources/js/components/DeviceImages.vue b/resources/js/components/DeviceImages.vue index d8ad6efe07..9342dbe3da 100644 --- a/resources/js/components/DeviceImages.vue +++ b/resources/js/components/DeviceImages.vue @@ -18,14 +18,10 @@ import DeviceImage from './DeviceImage' export default { components: {DeviceImage, FileUploader}, props: { - // This is deliberately not retrieved from the store as it can be the current, unsaved device. - device: { - type: Object, - required: true - }, - idevents: { + id: { type: Number, - required: true + required: false, + default: null }, add: { type: Boolean, @@ -45,33 +41,32 @@ export default { }, data () { return { - maxFiles: 5 + maxFiles: 5, + imagesDuringCreation: null, } }, computed: { - idtouse() { - return this.device ? this.device.iddevices : null - }, images() { // TODO LATER The images are currently added/removed/deleted immediately, and so we get them from the store. // This should be deferred until the save. - if (this.idtouse) { - return this.$store.getters['devices/imagesByDevice'](this.idtouse) + if (this.id > 0) { + return this.$store.getters['devices/imagesByDevice'](this.id) } else { - return [] + return this.imagesDuringCreation || [] } }, uploadURL() { - return '/device/image-upload/' + this.idtouse + return '/device/image-upload/' + (this.id ? this.id : 0) } }, methods: { uploaded(images) { - // We have uploaded some images. Add them to the store. - this.$store.dispatch('devices/setImages', { - iddevices: this.idtouse, - images: images - }) + if (this.id > 0) { + // We have uploaded some images. Fetch the device to reflect the new images.Add them to the store. + this.$store.dispatch('devices/fetch', this.id) + } else { + this.imagesDuringCreation = images + } } } } diff --git a/resources/js/components/DeviceReference.vue b/resources/js/components/DeviceReference.vue new file mode 100644 index 0000000000..e76e7d0159 --- /dev/null +++ b/resources/js/components/DeviceReference.vue @@ -0,0 +1,60 @@ + + + diff --git a/resources/js/components/DeviceRepairStatus.vue b/resources/js/components/DeviceRepairStatus.vue index 0d0744c3d1..b309e323bf 100644 --- a/resources/js/components/DeviceRepairStatus.vue +++ b/resources/js/components/DeviceRepairStatus.vue @@ -46,18 +46,16 @@ @@ -74,26 +72,24 @@ import { export default { props: { status: { - type: Number, + type: String, required: false, default: null }, parts: { - type: Number, + type: String, required: false, default: null }, steps: { - type: Number, + type: String, required: false, default: null }, - barriers: { - type: Array, + barrier: { + type: String, required: false, - default: function() { - return [] - } + default: null }, barrierList: { type: Array, @@ -106,13 +102,6 @@ export default { }, }, computed: { - translatedBarriers() { - return this.barrierList.map(b => { - var newb = JSON.parse(JSON.stringify(b)) - newb.barrier = this.$lang.get('strings.' + b.barrier) - return newb - }) - }, showSteps () { return this.status === REPAIRABLE }, @@ -204,22 +193,24 @@ export default { } ] }, - barriersValue: { - get() { - // We have an array of ids which we need to map to an array of options. - var ret = this.barrierList.filter(b => { - return this.barriers && this.barriers.indexOf(b.id) !== -1 - }) + barriersOptions() { + return this.barrierList.map(b => { + var newb = { + id: b.barrier, + text: this.$lang.get('strings.' + b.barrier) + } - return ret.map(b => { - return this.translatedBarriers.find(t => { - return t.id === b.id - }) + return newb + }) + }, + barrierValue: { + get() { + return this.barriersOptions.find(o => { + return o.id === this.barrier }) }, set(newval) { - // We have an array of options we want to emit as an array of ids. - this.$emit('update:barriers', newval.map(o => o.id)) + this.$emit('update:barrier', newval.id) } }, } diff --git a/resources/js/components/DeviceWeight.vue b/resources/js/components/DeviceWeight.vue index a5326fcce7..27c1a19166 100644 --- a/resources/js/components/DeviceWeight.vue +++ b/resources/js/components/DeviceWeight.vue @@ -4,7 +4,7 @@ {{ __('devices.weight') }} {{ info }} diff --git a/resources/js/components/EventDevice.vue b/resources/js/components/EventDevice.vue index 3377aab3fd..ce354e779f 100644 --- a/resources/js/components/EventDevice.vue +++ b/resources/js/components/EventDevice.vue @@ -28,15 +28,15 @@ :disabled="disabled"/> -

{{ __('devices.title_repair') }}

-
@@ -47,13 +47,6 @@ :disabled="disabled"/> -
- - -
@@ -65,20 +58,24 @@ {{ axiosError }}

-
- - {{ __('partials.add_device') }} - - - {{ __('partials.save') }} - - - {{ __('devices.delete_device') }} - - - - {{ __('partials.cancel') }} - +
+ +
+ + {{ __('partials.add_device') }} + + + {{ __('partials.save') }} + + + {{ __('devices.delete_device') }} + + + + {{ __('partials.cancel') }} + +
@@ -86,9 +83,6 @@