diff --git a/src/Acl/EsiRolesMap.php b/src/Acl/EsiRolesMap.php index a1e51a618..ece97b4f8 100644 --- a/src/Acl/EsiRolesMap.php +++ b/src/Acl/EsiRolesMap.php @@ -142,6 +142,9 @@ class EsiRolesMap 'corporation.summary', 'corporation.market', ], + 'Project Manager' => [ + 'corporation.projects', + ], ]; /** diff --git a/src/Config/Permissions/corporation.php b/src/Config/Permissions/corporation.php index 19d761460..94e4017aa 100644 --- a/src/Config/Permissions/corporation.php +++ b/src/Config/Permissions/corporation.php @@ -183,4 +183,8 @@ 'label' => 'web::permissions.corporation_tracking_label', 'description' => 'web::permissions.corporation_tracking_description', ], + 'projects' => [ + 'label' => 'web::permissions.corporation_projects_label', + 'description' => 'web::permissions.corporation_projects_description', + ], ]; diff --git a/src/Config/package.corporation.menu.php b/src/Config/package.corporation.menu.php index fc3327dab..205da95af 100644 --- a/src/Config/package.corporation.menu.php +++ b/src/Config/package.corporation.menu.php @@ -64,6 +64,14 @@ 'highlight_view' => 'industry', 'route' => 'seatcore::corporation.view.industry', ], + [ + 'name' => 'project', + 'label' => 'web::seat.project', + 'plural' => true, + 'permission' => 'corporation.projects', + 'highlight_view' => 'projects', + 'route' => 'seatcore::corporation.view.projects', + ], [ 'name' => 'killmails', 'label' => 'web::seat.killmails', diff --git a/src/Config/web.jobnames.php b/src/Config/web.jobnames.php index 6b530f6bc..d1fa35dd6 100644 --- a/src/Config/web.jobnames.php +++ b/src/Config/web.jobnames.php @@ -76,6 +76,7 @@ 'transactions' => \Seat\Eveapi\Jobs\Wallet\Corporation\Transactions::class, 'starbases' => \Seat\Eveapi\Jobs\Corporation\Starbases::class, 'structures' => \Seat\Eveapi\Jobs\Corporation\Structures::class, + 'projects' => \Seat\Eveapi\Jobs\CorporationProjects\Projects::class, ], 'alliance' => [ 'contacts' => [ diff --git a/src/Http/Controllers/Corporation/ProjectController.php b/src/Http/Controllers/Corporation/ProjectController.php new file mode 100644 index 000000000..76a360217 --- /dev/null +++ b/src/Http/Controllers/Corporation/ProjectController.php @@ -0,0 +1,169 @@ +addScope(new CorporationScope('corporation.projects', [$corporation->corporation_id])) + ->render('web::corporation.projects.list', compact('corporation')); + } + + /** + * @param \Seat\Eveapi\Models\Corporation\CorporationInfo $corporation + * @param string $project_id + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function getProject(CorporationInfo $corporation, string $project_id) + { + $project = CorporationProject::with('contributors', 'creator') + ->where('id', $project_id) + ->first(); + $config = $this->normalizeProjectConfiguration($project->configuration); + + return view('web::corporation.projects.modals.content', compact('corporation', 'project', 'config')); + } + + /** + * Normalize project configuration into an array of items: + * [ ['key'=>'...', 'label'=>'...', 'value'=>'...', 'description'=>null], ... ] + */ + protected function normalizeProjectConfiguration($configuration): array + { + if (is_string($configuration)) { + // sometimes stored as JSON string + $decoded = json_decode($configuration, true); + if (json_last_error() === JSON_ERROR_NONE) { + $configuration = $decoded; + } + } + + // If null or empty + if (empty($configuration)) { + return []; + } + + $items = []; + + // Case: configuration is an array of option objects + if (is_array($configuration) && array_values($configuration) === $configuration) { + foreach ($configuration as $i => $opt) { + if (is_array($opt)) { + $items[] = [ + 'key' => $opt['name'] ?? "option_{$i}", + 'label' => $opt['display_name'] ?? $opt['name'] ?? "Option {$i}", + 'value' => isset($opt['value']) ? $opt['value'] : (isset($opt['default']) ? $opt['default'] : null), + 'description' => $opt['description'] ?? null, + ]; + } else { + // primitive in array + $items[] = [ + 'key' => "option_{$i}", + 'label' => "Option {$i}", + 'value' => (string) $opt, + 'description' => null, + ]; + } + } + + return $items; + } + + // Case: configuration is an associative object/array of named options + if (is_array($configuration)) { + foreach ($configuration as $k => $v) { + if (is_array($v) || is_object($v)) { + $vArr = (array) $v; + $items[] = [ + 'key' => $k, + 'label' => $vArr['label'] ?? $vArr['name'] ?? $k, + 'value' => $vArr['value'] ?? $vArr['default'] ?? json_encode($vArr), + 'description' => $vArr['description'] ?? null, + ]; + } else { + $items[] = [ + 'key' => $k, + 'label' => $k, + 'value' => $v, + 'description' => null, + ]; + } + } + + return $items; + } + + // Case: object (stdClass) + if (is_object($configuration)) { + $arr = (array) $configuration; + foreach ($arr as $k => $v) { + if (is_object($v) || is_array($v)) { + $vArr = (array) $v; + $items[] = [ + 'key' => $k, + 'label' => $vArr['label'] ?? $vArr['name'] ?? $k, + 'value' => $vArr['value'] ?? $vArr['default'] ?? json_encode($vArr), + 'description' => $vArr['description'] ?? null, + ]; + } else { + $items[] = [ + 'key' => $k, + 'label' => $k, + 'value' => $v, + 'description' => null, + ]; + } + } + + return $items; + } + + // Fallback: stringify whatever it is + return [ + [ + 'key' => 'raw', + 'label' => 'Configuration', + 'value' => is_scalar($configuration) ? (string) $configuration : json_encode($configuration), + 'description' => null, + ], + ]; + } +} diff --git a/src/Http/DataTables/Corporation/Financial/ProjectDataTable.php b/src/Http/DataTables/Corporation/Financial/ProjectDataTable.php new file mode 100644 index 000000000..9c151d017 --- /dev/null +++ b/src/Http/DataTables/Corporation/Financial/ProjectDataTable.php @@ -0,0 +1,117 @@ +eloquent($this->applyScopes($this->query())) + ->editColumn('state', function ($row) { + return ucfirst(str_replace('_', ' ', $row->state)); + }) + ->addColumn('progress', function ($raw) { + $row = (object) [ + 'min' => 0, + 'value' => $raw->progress_current, + 'max' => $raw->progress_desired, + 'showval' => true, + ]; + + return view('web::partials.progress', compact('row'))->render(); + }) + ->addColumn('reward', function ($raw) use (&$rawColumns) { + if (is_null($raw->reward_initial) || $raw->reward_initial == 0) { + return trans('web::seat.no_reward'); + } else { + $row = (object) [ + 'min' => 0, + 'value' => $raw->reward_initial - $raw->reward_remaining, + 'max' => $raw->reward_initial, + 'showval' => true, + ]; + $rawColumns[] = 'reward'; + + return view('web::partials.progress', compact('row'))->render(); + } + }) + ->editColumn('action', function ($row) use ($rawColumns) { + return view('web::corporation.projects.buttons.action', compact('row'))->render(); + }) + ->rawColumns(['action', 'progress', 'reward']) + ->toJson(); + } + + /** + * @return \Yajra\DataTables\Html\Builder + */ + public function html() + { + return $this->builder() + ->postAjax() + ->columns($this->getColumns()) + ->addAction() + ->addTableClass('table-striped table-hover') + ->parameters([ + 'drawCallback' => 'function() { $("[data-toggle=tooltip]").tooltip(); }', + ]); + } + + /** + * @return \Illuminate\Database\Eloquent\Builder + */ + public function query() + { + return CorporationProject::withCount('contributors'); + } + + /** + * @return array + */ + public function getColumns() + { + return [ + ['data' => 'name', 'title' => trans_choice('web::seat.name', 1)], + ['data' => 'last_modified', 'title' => trans_choice('web::seat.last_modified', 1)], + ['data' => 'state', 'title' => trans('web::seat.state')], + ['data' => 'progress', 'title' => trans('web::seat.progress'), 'orderable' => false], + ['data' => 'reward', 'title' => trans('web::seat.reward'), 'orderable' => false], + ['data' => 'contributors_count', 'title' => trans_choice('web::seat.contributor', 2)], + ]; + } +} diff --git a/src/Http/Routes/Corporation/View.php b/src/Http/Routes/Corporation/View.php index 0ff225479..f087422d2 100644 --- a/src/Http/Routes/Corporation/View.php +++ b/src/Http/Routes/Corporation/View.php @@ -214,3 +214,13 @@ Route::post('/{corporation}/transactions/export') ->uses('WalletController@transactions') ->middleware('can:corporation.transaction,corporation'); + +Route::get('/{corporation}/projects') + ->name('seatcore::corporation.view.projects') + ->uses('ProjectController@getProjects') + ->middleware('can:corporation.projects,corporation'); + +Route::get('/{corporation}/project/{project_id}') + ->name('seatcore::corporation.view.projects.details') + ->uses('ProjectController@getProject') + ->middleware('can:corporation.projects,corporation'); diff --git a/src/WebServiceProvider.php b/src/WebServiceProvider.php index 430e02d81..4e8f0ac1d 100644 --- a/src/WebServiceProvider.php +++ b/src/WebServiceProvider.php @@ -582,6 +582,7 @@ private function register_settings() 'esi-universe.read_structures.v1', 'esi-wallet.read_character_wallet.v1', 'esi-wallet.read_corporation_wallets.v1', + 'esi-corporations.read_projects.v1', ], ], ]); diff --git a/src/resources/lang/en/seat.php b/src/resources/lang/en/seat.php index d23c6f797..429b406ae 100644 --- a/src/resources/lang/en/seat.php +++ b/src/resources/lang/en/seat.php @@ -44,6 +44,7 @@ 'id' => 'ID|IDs', 'type' => 'Type|Types', 'expiry' => 'Expiry', + 'no_expiry' => 'No Expiry', 'never' => 'Never', 'detail' => 'Detail|Details', 'delete' => 'Delete', @@ -58,12 +59,14 @@ 'owner' => 'Owner', 'general' => 'General', 'description' => 'Description', + 'no_description' => 'No Description', 'labels' => 'Labels', 'created' => 'Created', 'issuer' => 'Issuer', 'title' => 'Title|Titles', 'price' => 'Price', 'reward' => 'Reward', + 'no_reward' => 'No Reward', 'collateral' => 'Collateral', 'assignee' => 'Assignee', 'acceptor' => 'Acceptor', @@ -104,6 +107,7 @@ 'week' => 'week', 'day' => 'day', 'save' => 'Save', + 'unlimited' => 'Unlimited', // Requirements 'requirements' => 'Requirements', @@ -201,6 +205,8 @@ 'tracking' => 'Tracking', 'about' => 'About', 'market_browser' => 'Market Browser', + 'project' => 'Project|Projects', + 'last_modified' => 'Last Modified', 'assets' => 'Assets', 'location_flag' => 'Location Flag', @@ -496,6 +502,7 @@ 'update_transactions' => 'Update Transactions', 'update_wallet' => 'Update Wallet', 'update_loyalty_points' => 'Update Loyalty Points', + 'update_projects' => 'Update Projects', // Character 'joined_curr_corp' => 'Joined Current Corporation', @@ -770,4 +777,21 @@ 'sde_version' => 'SDE Version', 'render_in' => 'Rendered In', 'copyright' => 'Copyright', + + // Projects + 'contributor' => 'Contributor|Contributors', + 'contributed' => 'Contributed', + 'no_contributions' => 'No Contributions', + 'no_configuration_project' => 'No configuration available for this project', + 'project_setting' => 'Setting', + 'career' => 'Career', + + 'financial' => 'Financial', + 'reward_initial' => 'Initial Reward', + 'reward_remaining' => 'Remaining Reward', + 'contribution_reward' => 'Reward per Contribution', + 'contribution_rules' => 'Contribution Rules', + 'participation_limit' => 'Participation Limit', + 'submission_limit' => 'Submission Limit', + 'submission_multiplier' => 'Submission Multiplier', ]; diff --git a/src/resources/views/corporation/projects/buttons/action.blade.php b/src/resources/views/corporation/projects/buttons/action.blade.php new file mode 100644 index 000000000..9538c837f --- /dev/null +++ b/src/resources/views/corporation/projects/buttons/action.blade.php @@ -0,0 +1,2 @@ + +@include('web::corporation.projects.buttons.details') \ No newline at end of file diff --git a/src/resources/views/corporation/projects/buttons/details.blade.php b/src/resources/views/corporation/projects/buttons/details.blade.php new file mode 100644 index 000000000..918880b17 --- /dev/null +++ b/src/resources/views/corporation/projects/buttons/details.blade.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/src/resources/views/corporation/projects/list.blade.php b/src/resources/views/corporation/projects/list.blade.php new file mode 100644 index 000000000..cf58a2e59 --- /dev/null +++ b/src/resources/views/corporation/projects/list.blade.php @@ -0,0 +1,55 @@ +@extends('web::corporation.layouts.view', ['viewname' => 'projects', 'breadcrumb' => trans_choice('web::seat.project', 2)]) + +@section('page_header', trans_choice('web::seat.corporation', 1) . ' ' . trans_choice('web::seat.project', 2)) + +@inject('request', 'Illuminate\Http\Request') + +@section('corporation_content') + +
+ {!! $project->description ?: trans('web::seat.no_description') !!} +
+| {{ trans('web::seat.character') }} | +{{ trans('web::seat.contributed') }} | +{{ trans('web::seat.last_updated') }} | +
|---|---|---|
| + @include('web::partials.character', ['character' => $contributor->character]) + | +{{ number_format($contributor->contributed) }} | +{{ carbon($contributor->updated_at)->toDayDateTimeString() }} | +
| {{ trans('web::seat.no_contributions') }} | +||
' + . e(json_encode($item, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)) + . '
' + . e($value) + . ''; + } + + // Scalar + return e((string) $value); + }; + } +@endphp + +@if(empty($config)) +
{{ trans('web::seat.no_configuration_project') }}
+@else +| {{ trans('web::seat.project_setting') }} | +Value | +
|---|---|
+ {{ $prettyKey }}
+ |
+ + {!! $renderValue($value) !!} + | +