|
|
@@ -2,13 +2,11 @@
|
|
2
|
2
|
|
|
3
|
3
|
namespace App\Filament\Resources;
|
|
4
|
4
|
|
|
5
|
|
-use AbdulmajeedJamaan\FilamentTranslatableTabs\TranslatableTabs;
|
|
6
|
5
|
use App\Filament\Resources\ProjectResource\Pages;
|
|
7
|
|
-use App\Filament\Resources\ProjectResource\RelationManagers;
|
|
8
|
6
|
use App\Models\Badge;
|
|
9
|
7
|
use App\Models\Project;
|
|
10
|
8
|
use App\Models\Region;
|
|
11
|
|
-use Filament\Forms;
|
|
|
9
|
+use App\Service\DeepLService;
|
|
12
|
10
|
use Filament\Forms\Components\DatePicker;
|
|
13
|
11
|
use Filament\Forms\Components\FileUpload;
|
|
14
|
12
|
use Filament\Forms\Components\Group;
|
|
|
@@ -30,72 +28,75 @@ use Filament\Tables\Columns\TextColumn;
|
|
30
|
28
|
use Filament\Tables\Filters\SelectFilter;
|
|
31
|
29
|
use Filament\Tables\Table;
|
|
32
|
30
|
use Illuminate\Database\Eloquent\Builder;
|
|
33
|
|
-use Illuminate\Database\Eloquent\SoftDeletingScope;
|
|
34
|
31
|
use Illuminate\Support\Facades\Storage;
|
|
35
|
32
|
use Illuminate\Support\Str;
|
|
36
|
33
|
use SolutionForest\FilamentTranslateField\Forms\Component\Translate;
|
|
37
|
|
-use App\Service\DeepLService;
|
|
38
|
34
|
|
|
39
|
35
|
class ProjectResource extends Resource
|
|
40
|
36
|
{
|
|
41
|
37
|
protected static ?string $model = Project::class;
|
|
42
|
38
|
|
|
43
|
39
|
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
|
|
40
|
+
|
|
44
|
41
|
protected static ?string $navigationGroup = '專案項目管理';
|
|
45
|
|
- protected static ?string $navigationLabel = "專案項目管理";
|
|
46
|
|
- protected static ?string $modelLabel = "專案項目管理";
|
|
|
42
|
+
|
|
|
43
|
+ protected static ?string $navigationLabel = '專案項目管理';
|
|
|
44
|
+
|
|
|
45
|
+ protected static ?string $modelLabel = '專案項目管理';
|
|
|
46
|
+
|
|
47
|
47
|
protected static ?int $navigationSort = 5;
|
|
48
|
48
|
|
|
49
|
49
|
public static function form(Form $form): Form
|
|
50
|
50
|
{
|
|
51
|
51
|
return $form
|
|
52
|
52
|
->schema([
|
|
53
|
|
- Section::make("")->schema([
|
|
|
53
|
+ Section::make('')->schema([
|
|
54
|
54
|
Translate::make()->schema(fn (string $locale) => [
|
|
55
|
|
- TextInput::make("name")->label("項目名稱"),
|
|
56
|
|
- TextInput::make("sub_name")->label("項目子名稱"),
|
|
|
55
|
+ TextInput::make('name')->label('項目名稱'),
|
|
|
56
|
+ TextInput::make('sub_name')->label('項目子名稱'),
|
|
57
|
57
|
])
|
|
58
|
|
- ->locales(["zh_TW", "en"])
|
|
59
|
|
- ->actions([
|
|
60
|
|
- app(DeepLService::class)->createTranslationAction("Main", ["name", "sub_name"])
|
|
61
|
|
- ])->columnSpanFull()->id("main"),
|
|
|
58
|
+ ->locales(['zh_TW', 'en'])
|
|
|
59
|
+ ->actions([
|
|
|
60
|
+ app(DeepLService::class)->createTranslationAction('Main', ['name', 'sub_name']),
|
|
|
61
|
+ ])->columnSpanFull()->id('main'),
|
|
62
|
62
|
Select::make('tags')
|
|
63
|
63
|
->multiple()
|
|
64
|
64
|
->relationship('tags', 'name')
|
|
65
|
65
|
->preload()
|
|
66
|
66
|
->label('標籤'),
|
|
67
|
|
- FileUpload::make("img_url")->label("圖片")->directory("project")->multiple()->maxFiles(5),
|
|
68
|
|
- TextInput::make('order')->label("排序")->default("0")
|
|
|
67
|
+ FileUpload::make('thumbnail')->label('縮圖')->directory('project')->image(),
|
|
|
68
|
+ FileUpload::make('img_url')->label('圖片')->directory('project')->multiple()->maxFiles(5),
|
|
|
69
|
+ TextInput::make('order')->label('排序')->default('0'),
|
|
69
|
70
|
]),
|
|
70
|
71
|
Tabs::make()->schema([
|
|
71
|
|
- Tab::make("專案概要")->schema([
|
|
72
|
|
- Select::make("region_id")->label("地區")->options(function (){
|
|
73
|
|
- return Region::where("visible",true)->pluck("name", "id");
|
|
|
72
|
+ Tab::make('專案概要')->schema([
|
|
|
73
|
+ Select::make('region_id')->label('地區')->options(function () {
|
|
|
74
|
+ return Region::where('visible', true)->pluck('name', 'id');
|
|
74
|
75
|
}),
|
|
75
|
76
|
Translate::make()->schema(fn (string $locale) => [
|
|
76
|
|
- Textarea::make("summaries")->label("簡述"),
|
|
77
|
|
- TextInput::make("district")->label("區域"),
|
|
78
|
|
- TextInput::make("address")->label("地址"),
|
|
79
|
|
- Textarea::make("floor_plan")->label("樓層規劃"),
|
|
80
|
|
- Textarea::make("building_structure")->label("建築結構"),
|
|
81
|
|
- Textarea::make("design_unit")->label("設計單位")
|
|
|
77
|
+ Textarea::make('summaries')->label('簡述'),
|
|
|
78
|
+ TextInput::make('district')->label('區域'),
|
|
|
79
|
+ TextInput::make('address')->label('地址'),
|
|
|
80
|
+ Textarea::make('floor_plan')->label('樓層規劃'),
|
|
|
81
|
+ Textarea::make('building_structure')->label('建築結構'),
|
|
|
82
|
+ Textarea::make('design_unit')->label('設計單位'),
|
|
82
|
83
|
])
|
|
83
|
|
- ->locales(["zh_TW", "en"])
|
|
84
|
|
- ->actions([
|
|
85
|
|
- app(DeepLService::class)->createTranslationAction("summaries", ["summaries","address",
|
|
86
|
|
- "floor_plan","building_structure","design_unit"])
|
|
87
|
|
- ])->columnSpanFull()->id("summaries"),
|
|
88
|
|
- Radio::make("badge_type")->label("")->options([1 => "永續目標", 2 => "取得標章"])->default(1)->inline(),
|
|
89
|
|
- Repeater::make("badgesTarget")->label("永續目標")->schema([
|
|
90
|
|
- Hidden::make("award_type")->default(1),
|
|
|
84
|
+ ->locales(['zh_TW', 'en'])
|
|
|
85
|
+ ->actions([
|
|
|
86
|
+ app(DeepLService::class)->createTranslationAction('summaries', ['summaries', 'address',
|
|
|
87
|
+ 'floor_plan', 'building_structure', 'design_unit']),
|
|
|
88
|
+ ])->columnSpanFull()->id('summaries'),
|
|
|
89
|
+ Radio::make('badge_type')->label('')->options([1 => '永續目標', 2 => '取得標章'])->default(1)->inline(),
|
|
|
90
|
+ Repeater::make('badgesTarget')->label('永續目標')->schema([
|
|
|
91
|
+ Hidden::make('award_type')->default(1),
|
|
91
|
92
|
Select::make('badge_id')
|
|
92
|
93
|
->options(function () {
|
|
93
|
94
|
return Badge::all()->mapWithKeys(function ($badge) {
|
|
94
|
95
|
return [
|
|
95
|
96
|
$badge->id => '<div class="flex items-center gap-2">
|
|
96
|
|
- <img src="' . Storage::url($badge->img_url) . '" class="w-6 h-6 rounded-full">
|
|
97
|
|
- <span>' . $badge->title . '</span>
|
|
98
|
|
- </div>'
|
|
|
97
|
+ <img src="'.Storage::url($badge->img_url).'" class="w-6 h-6 rounded-full">
|
|
|
98
|
+ <span>'.$badge->title.'</span>
|
|
|
99
|
+ </div>',
|
|
99
|
100
|
];
|
|
100
|
101
|
});
|
|
101
|
102
|
})
|
|
|
@@ -105,16 +106,16 @@ class ProjectResource extends Resource
|
|
105
|
106
|
->label('')
|
|
106
|
107
|
->required(),
|
|
107
|
108
|
])->reorderable(false),
|
|
108
|
|
- Repeater::make("badgesAward")->label("取得標章")->schema([
|
|
109
|
|
- Hidden::make("award_type")->default(2),
|
|
|
109
|
+ Repeater::make('badgesAward')->label('取得標章')->schema([
|
|
|
110
|
+ Hidden::make('award_type')->default(2),
|
|
110
|
111
|
Select::make('badge_id')
|
|
111
|
112
|
->options(function () {
|
|
112
|
113
|
return Badge::all()->mapWithKeys(function ($badge) {
|
|
113
|
114
|
return [
|
|
114
|
115
|
$badge->id => '<div class="flex items-center gap-2">
|
|
115
|
|
- <img src="' . Storage::url($badge->img_url) . '" class="w-6 h-6 rounded-full">
|
|
116
|
|
- <span>' . $badge->title . '</span>
|
|
117
|
|
- </div>'
|
|
|
116
|
+ <img src="'.Storage::url($badge->img_url).'" class="w-6 h-6 rounded-full">
|
|
|
117
|
+ <span>'.$badge->title.'</span>
|
|
|
118
|
+ </div>',
|
|
118
|
119
|
];
|
|
119
|
120
|
});
|
|
120
|
121
|
})
|
|
|
@@ -128,67 +129,67 @@ class ProjectResource extends Resource
|
|
128
|
129
|
->format('Y-m')
|
|
129
|
130
|
->displayFormat('Y年m月')
|
|
130
|
131
|
->native(false)
|
|
131
|
|
- ->closeOnDateSelection()
|
|
|
132
|
+ ->closeOnDateSelection(),
|
|
132
|
133
|
])->reorderable(false),
|
|
133
|
134
|
]),
|
|
134
|
|
- Tab::make("開發歷程")->schema([
|
|
135
|
|
- Repeater::make("histories")->label("")->schema([
|
|
|
135
|
+ Tab::make('開發歷程')->schema([
|
|
|
136
|
+ Repeater::make('histories')->label('')->schema([
|
|
136
|
137
|
TextInput::make('histories_item_key')
|
|
137
|
|
- ->default(fn () => Str::random())
|
|
138
|
|
- ->hidden()
|
|
139
|
|
- ->afterStateHydrated(function (TextInput $component, $state) {
|
|
140
|
|
- if (empty($state)) {
|
|
141
|
|
- $component->state(Str::random());
|
|
142
|
|
- }
|
|
143
|
|
- }),
|
|
144
|
|
- DatePicker::make("operate_date")->label("歷程日期")->columnSpan(1),
|
|
|
138
|
+ ->default(fn () => Str::random())
|
|
|
139
|
+ ->hidden()
|
|
|
140
|
+ ->afterStateHydrated(function (TextInput $component, $state) {
|
|
|
141
|
+ if (empty($state)) {
|
|
|
142
|
+ $component->state(Str::random());
|
|
|
143
|
+ }
|
|
|
144
|
+ }),
|
|
|
145
|
+ DatePicker::make('operate_date')->label('歷程日期')->columnSpan(1),
|
|
145
|
146
|
Translate::make()->schema(fn (string $locale) => [
|
|
146
|
|
- TextInput::make("title")->label("標題")->columnSpanFull()
|
|
|
147
|
+ TextInput::make('title')->label('標題')->columnSpanFull(),
|
|
147
|
148
|
])
|
|
148
|
|
- ->locales(["zh_TW", "en"])
|
|
149
|
|
- ->actions([
|
|
150
|
|
- app(DeepLService::class)->createTranslationAction("histories", ["title"])
|
|
151
|
|
- ])->columnSpanFull()
|
|
152
|
|
- ->id(fn ($get) => "histories_" . $get('histories_item_key')),
|
|
|
149
|
+ ->locales(['zh_TW', 'en'])
|
|
|
150
|
+ ->actions([
|
|
|
151
|
+ app(DeepLService::class)->createTranslationAction('histories', ['title']),
|
|
|
152
|
+ ])->columnSpanFull()
|
|
|
153
|
+ ->id(fn ($get) => 'histories_'.$get('histories_item_key')),
|
|
153
|
154
|
])
|
|
154
|
|
- ->relationship("histories", function ($query,Get $get, $livewire) {
|
|
155
|
|
- return $query->orderby("operate_date","desc");
|
|
156
|
|
- })
|
|
157
|
|
- ->collapsible()
|
|
158
|
|
- ->cloneable()
|
|
|
155
|
+ ->relationship('histories', function ($query, Get $get, $livewire) {
|
|
|
156
|
+ return $query->orderby('operate_date', 'desc');
|
|
|
157
|
+ })
|
|
|
158
|
+ ->collapsible()
|
|
|
159
|
+ ->cloneable(),
|
|
159
|
160
|
]),
|
|
160
|
|
- Tab::make("空間資訊")->schema([
|
|
|
161
|
+ Tab::make('空間資訊')->schema([
|
|
161
|
162
|
Group::make()->schema([
|
|
162
|
163
|
Translate::make()->schema(fn (string $locale) => [
|
|
163
|
|
- TextInput::make("contact_unit")->label("物管單位"),
|
|
164
|
|
- TextInput::make("contact_phone")->label("物管電話"),
|
|
165
|
|
- TextInput::make("inversment_phone")->label("招商電話"),
|
|
|
164
|
+ TextInput::make('contact_unit')->label('物管單位'),
|
|
|
165
|
+ TextInput::make('contact_phone')->label('物管電話'),
|
|
|
166
|
+ TextInput::make('inversment_phone')->label('招商電話'),
|
|
166
|
167
|
])
|
|
167
|
|
- ->locales(["zh_TW", "en"])
|
|
168
|
|
- ->actions([
|
|
169
|
|
- app(DeepLService::class)->createTranslationAction("contact", ["contact_unit", "contact_phone", "inversment_phone"])
|
|
170
|
|
- ])->columnSpanFull()->columns(3)
|
|
171
|
|
- ->id("contact"),
|
|
172
|
|
- TextInput::make("offical_link")->label("官網連結"),
|
|
|
168
|
+ ->locales(['zh_TW', 'en'])
|
|
|
169
|
+ ->actions([
|
|
|
170
|
+ app(DeepLService::class)->createTranslationAction('contact', ['contact_unit', 'contact_phone', 'inversment_phone']),
|
|
|
171
|
+ ])->columnSpanFull()->columns(3)
|
|
|
172
|
+ ->id('contact'),
|
|
|
173
|
+ TextInput::make('offical_link')->label('官網連結'),
|
|
173
|
174
|
])->columnSpanFull(),
|
|
174
|
|
- Repeater::make("spaceInfos")->label("")->schema([
|
|
|
175
|
+ Repeater::make('spaceInfos')->label('')->schema([
|
|
175
|
176
|
Translate::make()->schema(fn (string $locale) => [
|
|
176
|
|
- TextInput::make("title")->label("標題")->columnSpanFull(),
|
|
177
|
|
- Textarea::make("content")->label("內文")->columnSpanFull()
|
|
|
177
|
+ TextInput::make('title')->label('標題')->columnSpanFull(),
|
|
|
178
|
+ Textarea::make('content')->label('內文')->columnSpanFull(),
|
|
178
|
179
|
])
|
|
179
|
|
- ->locales(["zh_TW", "en"])
|
|
180
|
|
- ->actions([
|
|
181
|
|
- app(DeepLService::class)->createTranslationAction("spaceInfos", ["title", "content"])
|
|
182
|
|
- ])->columnSpanFull()
|
|
183
|
|
- ->id(fn ($get) => "spaceInfos_" . $get('item_key')),
|
|
|
180
|
+ ->locales(['zh_TW', 'en'])
|
|
|
181
|
+ ->actions([
|
|
|
182
|
+ app(DeepLService::class)->createTranslationAction('spaceInfos', ['title', 'content']),
|
|
|
183
|
+ ])->columnSpanFull()
|
|
|
184
|
+ ->id(fn ($get) => 'spaceInfos_'.$get('item_key')),
|
|
184
|
185
|
])
|
|
185
|
|
- ->relationship("spaceInfos")
|
|
186
|
|
- ->label("")
|
|
187
|
|
- ->collapsible()
|
|
188
|
|
- ->reorderableWithButtons()
|
|
189
|
|
- ->orderColumn('order')
|
|
190
|
|
- ->cloneable(),
|
|
191
|
|
- ])
|
|
|
186
|
+ ->relationship('spaceInfos')
|
|
|
187
|
+ ->label('')
|
|
|
188
|
+ ->collapsible()
|
|
|
189
|
+ ->reorderableWithButtons()
|
|
|
190
|
+ ->orderColumn('order')
|
|
|
191
|
+ ->cloneable(),
|
|
|
192
|
+ ]),
|
|
192
|
193
|
])->columnSpanFull(),
|
|
193
|
194
|
]);
|
|
194
|
195
|
}
|
|
|
@@ -198,47 +199,47 @@ class ProjectResource extends Resource
|
|
198
|
199
|
return $table
|
|
199
|
200
|
->columns([
|
|
200
|
201
|
//
|
|
201
|
|
- TextColumn::make("name")->label("項目名稱")->alignCenter(),
|
|
202
|
|
- ImageColumn::make("first_list_img_url")->label("列表圖")->alignCenter(),
|
|
203
|
|
- TextColumn::make("region.name")->label("地址")->alignCenter()
|
|
204
|
|
- ->formatStateUsing(fn ($record) => $record->region->getTranslation("name", "zh_TW") . ' | ' . $record->getTranslation("address", "zh_TW")),
|
|
|
202
|
+ TextColumn::make('name')->label('項目名稱')->alignCenter(),
|
|
|
203
|
+ ImageColumn::make('thumbnail_url')->label('縮圖')->alignCenter(),
|
|
|
204
|
+ ImageColumn::make('first_list_img_url')->label('列表圖')->alignCenter(),
|
|
|
205
|
+ TextColumn::make('region.name')->label('地址')->alignCenter()
|
|
|
206
|
+ ->formatStateUsing(fn ($record) => $record->region->getTranslation('name', 'zh_TW').' | '.$record->getTranslation('address', 'zh_TW')),
|
|
205
|
207
|
])
|
|
206
|
208
|
->filters([
|
|
207
|
|
- SelectFilter::make('visible')->label("上/下架")
|
|
208
|
|
- ->options([
|
|
209
|
|
- 0 => "下架",
|
|
210
|
|
- 1 => "上架",
|
|
211
|
|
- ])
|
|
212
|
|
- ->query(
|
|
213
|
|
- fn (array $data, Builder $query): Builder =>
|
|
214
|
|
- $query->when(
|
|
215
|
|
- $data['value'],
|
|
216
|
|
- fn (Builder $query, $value): Builder => $query->where('visible', $data['value'])
|
|
217
|
|
- )
|
|
218
|
|
- ),
|
|
|
209
|
+ SelectFilter::make('visible')->label('上/下架')
|
|
|
210
|
+ ->options([
|
|
|
211
|
+ 0 => '下架',
|
|
|
212
|
+ 1 => '上架',
|
|
|
213
|
+ ])
|
|
|
214
|
+ ->query(
|
|
|
215
|
+ fn (array $data, Builder $query): Builder => $query->when(
|
|
|
216
|
+ $data['value'],
|
|
|
217
|
+ fn (Builder $query, $value): Builder => $query->where('visible', $data['value'])
|
|
|
218
|
+ )
|
|
|
219
|
+ ),
|
|
219
|
220
|
])
|
|
220
|
221
|
->actions([
|
|
221
|
222
|
Tables\Actions\EditAction::make(),
|
|
222
|
223
|
Tables\Actions\DeleteAction::make(),
|
|
223
|
|
- \Filament\Tables\Actions\Action::make("audit")
|
|
224
|
|
- ->label(fn ($record) => match ($record->visible) {
|
|
225
|
|
- 0 => '上架',
|
|
226
|
|
- 1 => '下架',
|
|
227
|
|
- })
|
|
228
|
|
- ->color(fn ($record) => match ($record->visible) {
|
|
229
|
|
- 0 => 'warning',
|
|
230
|
|
- 1 => 'gray',
|
|
231
|
|
- })
|
|
232
|
|
- ->icon(fn ($record) => match ($record->visible) {
|
|
233
|
|
- 0 => 'heroicon-m-chevron-double-up',
|
|
234
|
|
- 1 => 'heroicon-m-chevron-double-down',
|
|
235
|
|
- })
|
|
236
|
|
- ->action(function ($record): void {
|
|
237
|
|
- $record->visible = !$record->visible;
|
|
238
|
|
- $record->save();
|
|
239
|
|
- })
|
|
240
|
|
- ->outlined()
|
|
241
|
|
- ->requiresConfirmation(),
|
|
|
224
|
+ \Filament\Tables\Actions\Action::make('audit')
|
|
|
225
|
+ ->label(fn ($record) => match ($record->visible) {
|
|
|
226
|
+ 0 => '上架',
|
|
|
227
|
+ 1 => '下架',
|
|
|
228
|
+ })
|
|
|
229
|
+ ->color(fn ($record) => match ($record->visible) {
|
|
|
230
|
+ 0 => 'warning',
|
|
|
231
|
+ 1 => 'gray',
|
|
|
232
|
+ })
|
|
|
233
|
+ ->icon(fn ($record) => match ($record->visible) {
|
|
|
234
|
+ 0 => 'heroicon-m-chevron-double-up',
|
|
|
235
|
+ 1 => 'heroicon-m-chevron-double-down',
|
|
|
236
|
+ })
|
|
|
237
|
+ ->action(function ($record): void {
|
|
|
238
|
+ $record->visible = ! $record->visible;
|
|
|
239
|
+ $record->save();
|
|
|
240
|
+ })
|
|
|
241
|
+ ->outlined()
|
|
|
242
|
+ ->requiresConfirmation(),
|
|
242
|
243
|
])
|
|
243
|
244
|
->bulkActions([
|
|
244
|
245
|
Tables\Actions\BulkActionGroup::make([
|