Skip to content

Commit 186fcd2

Browse files
authored
Merge pull request #140 from NativePHP/filament-panel
Init filament panel
2 parents 94d0e5c + 72bd801 commit 186fcd2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+3763
-50
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,5 @@ ANYSTACK_PRO_POLICY_ID=
8080
ANYSTACK_MAX_POLICY_ID=
8181
ANYSTACK_FOREVER_POLICY_ID=
8282
ANYSTACK_TRIAL_POLICY_ID=
83+
84+
FILAMENT_USERS=

app/Filament/Pages/Dashboard.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace App\Filament\Pages;
4+
5+
use App\Filament\Widgets\LicenseDistributionChart;
6+
use App\Filament\Widgets\LicensesChart;
7+
use App\Filament\Widgets\StatsOverview;
8+
use App\Filament\Widgets\UsersChart;
9+
use Filament\Pages\Dashboard as BaseDashboard;
10+
11+
class Dashboard extends BaseDashboard
12+
{
13+
protected static ?string $navigationIcon = 'heroicon-o-home';
14+
15+
public function getHeaderWidgets(): array
16+
{
17+
return [
18+
StatsOverview::class,
19+
];
20+
}
21+
22+
public function getWidgets(): array
23+
{
24+
return [
25+
UsersChart::class,
26+
LicensesChart::class,
27+
LicenseDistributionChart::class,
28+
];
29+
}
30+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
<?php
2+
3+
namespace App\Filament\Resources;
4+
5+
use App\Filament\Resources\LicenseResource\Pages;
6+
use App\Models\License;
7+
use Filament\Forms;
8+
use Filament\Forms\Form;
9+
use Filament\Infolists\Components;
10+
use Filament\Infolists\Infolist;
11+
use Filament\Resources\Resource;
12+
use Filament\Tables;
13+
use Filament\Tables\Table;
14+
15+
class LicenseResource extends Resource
16+
{
17+
protected static ?string $model = License::class;
18+
19+
protected static ?string $navigationIcon = 'heroicon-o-key';
20+
21+
public static function form(Form $form): Form
22+
{
23+
return $form
24+
->schema([
25+
Forms\Components\Section::make('License Information')
26+
->schema([
27+
Forms\Components\TextInput::make('id')
28+
->disabled(),
29+
Forms\Components\TextInput::make('anystack_id')
30+
->maxLength(36)
31+
->disabled(),
32+
Forms\Components\Select::make('user_id')
33+
->relationship('user', 'email')
34+
->disabled(),
35+
Forms\Components\TextInput::make('policy_name')
36+
->label('Plan')
37+
->disabled(),
38+
Forms\Components\TextInput::make('key')
39+
->disabled(),
40+
Forms\Components\DateTimePicker::make('expires_at')
41+
->disabled(),
42+
Forms\Components\DateTimePicker::make('created_at')
43+
->disabled(),
44+
])->columns(2),
45+
]);
46+
}
47+
48+
public static function table(Table $table): Table
49+
{
50+
return $table
51+
->columns([
52+
Tables\Columns\TextColumn::make('id')
53+
->sortable()
54+
->searchable(),
55+
Tables\Columns\TextColumn::make('user.email')
56+
->searchable()
57+
->sortable()
58+
->copyable()
59+
->url(fn (\App\Models\License $record): string => route('filament.admin.resources.users.edit', ['record' => $record->user_id]))
60+
->openUrlInNewTab(),
61+
Tables\Columns\TextColumn::make('key')
62+
->searchable()
63+
->copyable(),
64+
Tables\Columns\TextColumn::make('policy_name')
65+
->label('Plan')
66+
->searchable(),
67+
Tables\Columns\TextColumn::make('expires_at')
68+
->dateTime()
69+
->sortable(),
70+
Tables\Columns\TextColumn::make('created_at')
71+
->dateTime()
72+
->sortable(),
73+
])
74+
->filters([
75+
//
76+
])
77+
->actions([
78+
Tables\Actions\ViewAction::make()
79+
->slideOver()
80+
->modalHeading('License Details')
81+
->modalWidth('7xl')
82+
->extraModalFooterActions([
83+
Tables\Actions\Action::make('viewUser')
84+
->label('View User')
85+
->icon('heroicon-o-user')
86+
->color('primary')
87+
->url(fn (License $record) => route('filament.admin.resources.users.edit', ['record' => $record->user_id]))
88+
->openUrlInNewTab()
89+
->visible(fn (License $record) => $record->user_id !== null),
90+
]),
91+
])
92+
->defaultPaginationPageOption(25)
93+
->bulkActions([]);
94+
}
95+
96+
public static function getRelations(): array
97+
{
98+
return [
99+
//
100+
];
101+
}
102+
103+
public static function infolist(Infolist $infolist): Infolist
104+
{
105+
return $infolist
106+
->schema([
107+
Components\Section::make('License Information')
108+
->schema([
109+
Components\TextEntry::make('id'),
110+
Components\TextEntry::make('key')
111+
->copyable(),
112+
Components\TextEntry::make('policy_name')
113+
->label('Plan'),
114+
Components\TextEntry::make('expires_at')
115+
->dateTime(),
116+
Components\TextEntry::make('created_at')
117+
->dateTime(),
118+
Components\TextEntry::make('anystack_id')
119+
->copyable(),
120+
])->columns(2),
121+
122+
Components\Section::make('User Information')
123+
->schema([
124+
Components\TextEntry::make('user.id')
125+
->label('User ID')
126+
->url(fn ($record) => route('filament.admin.resources.users.edit', ['record' => $record->user_id]))
127+
->openUrlInNewTab(),
128+
Components\TextEntry::make('user.email')
129+
->label('Email')
130+
->copyable()
131+
->url(fn ($record) => route('filament.admin.resources.users.edit', ['record' => $record->user_id]))
132+
->openUrlInNewTab(),
133+
Components\TextEntry::make('user.name')
134+
->label('Name'),
135+
Components\TextEntry::make('user.first_name')
136+
->label('First Name'),
137+
Components\TextEntry::make('user.last_name')
138+
->label('Last Name'),
139+
Components\TextEntry::make('user.stripe_id')
140+
->label('Stripe ID')
141+
->copyable()
142+
->visible(fn ($record) => filled($record->user->stripe_id))
143+
->url(fn ($record) => filled($record->user->stripe_id)
144+
? "https://dashboard.stripe.com/customers/{$record->user->stripe_id}"
145+
: null)
146+
->openUrlInNewTab(),
147+
Components\TextEntry::make('user.anystack_contact_id')
148+
->label('Anystack Contact ID')
149+
->copyable()
150+
->visible(fn ($record) => filled($record->user->anystack_contact_id))
151+
->url(fn ($record) => filled($record->user->anystack_contact_id)
152+
? "https://app.anystack.sh/contacts/{$record->user->anystack_contact_id}"
153+
: null)
154+
->openUrlInNewTab(),
155+
])->columns(2),
156+
157+
Components\Section::make('Subscription Information')
158+
->schema([
159+
Components\TextEntry::make('subscriptionItem.id')
160+
->label('Subscription Item ID')
161+
->visible(fn ($record) => $record->subscription_item_id !== null),
162+
Components\TextEntry::make('subscriptionItem.stripe_id')
163+
->label('Stripe Subscription Item ID')
164+
->copyable()
165+
->visible(fn ($record) => $record->subscription_item_id !== null),
166+
Components\TextEntry::make('subscriptionItem.subscription.stripe_id')
167+
->label('Stripe Subscription ID')
168+
->copyable()
169+
->visible(fn ($record) => $record->subscription_item_id !== null)
170+
->url(fn ($record) => $record->subscription_item_id !== null && filled($record->subscriptionItem?->subscription?->stripe_id)
171+
? "https://dashboard.stripe.com/subscriptions/{$record->subscriptionItem->subscription->stripe_id}"
172+
: null)
173+
->openUrlInNewTab(),
174+
Components\TextEntry::make('subscriptionItem.stripe_price')
175+
->label('Stripe Price ID')
176+
->copyable()
177+
->visible(fn ($record) => $record->subscription_item_id !== null),
178+
Components\TextEntry::make('subscriptionItem.stripe_product')
179+
->label('Stripe Product ID')
180+
->copyable()
181+
->visible(fn ($record) => $record->subscription_item_id !== null),
182+
Components\TextEntry::make('subscriptionItem.subscription.stripe_status')
183+
->label('Subscription Status')
184+
->badge()
185+
->color(fn ($state): string => match ($state) {
186+
'active' => 'success',
187+
'canceled' => 'danger',
188+
'incomplete' => 'warning',
189+
'incomplete_expired' => 'danger',
190+
'past_due' => 'warning',
191+
'trialing' => 'info',
192+
'unpaid' => 'danger',
193+
default => 'gray',
194+
})
195+
->visible(fn ($record) => $record->subscription_item_id !== null),
196+
])->columns(2)
197+
->visible(fn ($record) => $record->subscription_item_id !== null),
198+
]);
199+
}
200+
201+
public static function getPages(): array
202+
{
203+
return [
204+
'index' => Pages\ListLicenses::route('/'),
205+
];
206+
}
207+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\LicenseResource\Pages;
4+
5+
use App\Filament\Resources\LicenseResource;
6+
use Filament\Resources\Pages\ListRecords;
7+
8+
class ListLicenses extends ListRecords
9+
{
10+
protected static string $resource = LicenseResource::class;
11+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace App\Filament\Resources;
4+
5+
use App\Enums\Subscription as SubscriptionEnum;
6+
use Filament\Forms;
7+
use Filament\Forms\Form;
8+
use Filament\Resources\Resource;
9+
use Filament\Tables;
10+
use Filament\Tables\Table;
11+
use Laravel\Cashier\SubscriptionItem;
12+
13+
class SubscriptionItemResource extends Resource
14+
{
15+
protected static ?string $model = SubscriptionItem::class;
16+
17+
protected static ?string $navigationGroup = 'Billing';
18+
19+
protected static ?string $navigationLabel = 'Subscription Items';
20+
21+
protected static ?int $navigationSort = 2;
22+
23+
public static function form(Form $form): Form
24+
{
25+
return $form
26+
->schema([
27+
Forms\Components\Section::make('Subscription Item Details')
28+
->schema([
29+
Forms\Components\Select::make('subscription_id')
30+
->relationship('subscription', 'id')
31+
->searchable()
32+
->disabled(),
33+
Forms\Components\TextInput::make('stripe_id')
34+
->disabled(),
35+
Forms\Components\TextInput::make('stripe_product')
36+
->disabled(),
37+
Forms\Components\TextInput::make('stripe_price')
38+
->disabled(),
39+
Forms\Components\TextInput::make('quantity')
40+
->disabled(),
41+
])->columns(2),
42+
]);
43+
}
44+
45+
public static function table(Table $table): Table
46+
{
47+
return $table
48+
->columns([
49+
Tables\Columns\TextColumn::make('id')
50+
->sortable(),
51+
Tables\Columns\TextColumn::make('subscription.id')
52+
->numeric()
53+
->sortable(),
54+
Tables\Columns\TextColumn::make('stripe_id')
55+
->searchable()
56+
->copyable(),
57+
Tables\Columns\TextColumn::make('stripe_product')
58+
->searchable()
59+
->copyable(),
60+
Tables\Columns\TextColumn::make('stripe_price')
61+
->label('Plan')
62+
->formatStateUsing(function ($state) {
63+
try {
64+
return SubscriptionEnum::fromStripePriceId($state)->name();
65+
} catch (\Exception $e) {
66+
return $state;
67+
}
68+
})
69+
->searchable()
70+
->sortable(),
71+
Tables\Columns\TextColumn::make('quantity')
72+
->numeric()
73+
->sortable(),
74+
Tables\Columns\TextColumn::make('created_at')
75+
->dateTime()
76+
->sortable()
77+
->toggleable(isToggledHiddenByDefault: true),
78+
Tables\Columns\TextColumn::make('updated_at')
79+
->dateTime()
80+
->sortable()
81+
->toggleable(isToggledHiddenByDefault: true),
82+
])
83+
->filters([
84+
//
85+
])
86+
->actions([
87+
Tables\Actions\ViewAction::make(),
88+
Tables\Actions\Action::make('view_on_stripe')
89+
->label('View on Stripe')
90+
->color('gray')
91+
->icon('heroicon-o-arrow-top-right-on-square')
92+
->url(fn (SubscriptionItem $record) => 'https://dashboard.stripe.com/subscriptions/'.$record->subscription->stripe_id)
93+
->openUrlInNewTab(),
94+
])
95+
->bulkActions([
96+
//
97+
]);
98+
}
99+
100+
public static function getRelations(): array
101+
{
102+
return [
103+
//
104+
];
105+
}
106+
107+
public static function getPages(): array
108+
{
109+
return [
110+
'index' => SubscriptionItemResource\Pages\ListSubscriptionItems::route('/'),
111+
'view' => SubscriptionItemResource\Pages\ViewSubscriptionItem::route('/{record}'),
112+
];
113+
}
114+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\SubscriptionItemResource\Pages;
4+
5+
use App\Filament\Resources\SubscriptionItemResource;
6+
use Filament\Resources\Pages\ListRecords;
7+
8+
class ListSubscriptionItems extends ListRecords
9+
{
10+
protected static string $resource = SubscriptionItemResource::class;
11+
}

0 commit comments

Comments
 (0)