Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ indent_size = 2

[*.config.js]
indent_size = 2

[generated.d.ts]
indent_size = 2
21 changes: 10 additions & 11 deletions demo/app/Sharp/Authors/AuthorList.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,7 @@ function (Builder $builder) {
}
},
)

// Handle sorting
->when(
$this->queryParams->sortedBy() === 'email',
function (Builder $builder) {
$builder->orderBy('email', $this->queryParams->sortedDir());
},
function (Builder $builder) {
$builder->orderBy('name', $this->queryParams->sortedDir() ?: 'asc');
},
);
->orderBy($this->queryParams->sortedBy(), $this->queryParams->sortedDir());

return $this
->setCustomTransformer('avatar', (new SharpUploadModelThumbnailUrlTransformer(100))->renderAsImageTag())
Expand All @@ -101,6 +91,15 @@ function (Builder $builder) {
default => 'Unknown',
};
})
->setCustomTransformer('email', function ($value, User $user) {
return $user->hasVerifiedEmail()
? $value
: sprintf(
'<div>%s</div><div style="color: darkorange">%s pending invitation...</div>',
$value,
svg('far-envelope')->toHtml()
);
})
->transform($users->get());
}
}
27 changes: 21 additions & 6 deletions demo/app/Sharp/Authors/Commands/InviteUserCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace App\Sharp\Authors\Commands;

use App\Models\User;
use Code16\Sharp\EntityList\Commands\EntityCommand;
use Code16\Sharp\Form\Fields\SharpFormTextField;
use Code16\Sharp\Utils\Fields\FieldsContainer;
use Illuminate\Support\Str;

class InviteUserCommand extends EntityCommand
{
Expand All @@ -23,20 +25,33 @@ public function buildCommandConfig(): void

public function buildFormFields(FieldsContainer $formFields): void
{
$formFields->addField(
SharpFormTextField::make('email')
->setLabel('Email'),
);
$formFields
->addField(
SharpFormTextField::make('email')
->setLabel('Email'),
)
->addField(
SharpFormTextField::make('name')
->setLabel('Name'),
);
}

public function execute(array $data = []): array
{
$this->validate($data, [
'email' => ['required', 'email'],
'email' => ['required', 'email', 'max:100', 'unique:users,email'],
'name' => ['required', 'string', 'max:100'],
]);

User::create([
'email' => $data['email'],
'name' => $data['name'],
'password' => bcrypt(Str::random()),
'role' => 'editor',
]);

// Here we send an invitation, or something

return $this->info('Invitation sent!');
return $this->info('Invitation sent!', reload: true);
}
}
12 changes: 10 additions & 2 deletions demo/app/Sharp/Authors/Commands/VisitFacebookProfileCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@

namespace App\Sharp\Authors\Commands;

use App\Models\User;
use Code16\Sharp\EntityList\Commands\InstanceCommand;

class VisitFacebookProfileCommand extends InstanceCommand
{
public function label(): string
{
return "Visit author's facebook profile";
return 'Visit authors facebook profile';
}

public function buildCommandConfig(): void
{
$this->configureDescription('You will leave sharp');
$this->configureDescription('You will leave Sharp');
}

public function execute(mixed $instanceId, array $data = []): array
{
return $this->link('https://facebook.com');
}

public function authorizeFor(mixed $instanceId): bool
{
return sharp()->context()
->findListInstance($instanceId, fn ($id) => User::find($id))
->hasVerifiedEmail();
}
}
2 changes: 1 addition & 1 deletion docs/guide/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Here is the full list of available methods:

Finally, let's review the return possibilities: after a Command has been executed, the code must return something to tell to the front what to do next. There are height of them:

- `return $this->info('some text')`: displays the entered text in a modal.
- `return $this->info('some text', reload: true)`: displays the entered text in a modal. The second argument, optional (default is `false`), is a boolean to also mark Sharp to reload the page.
- `return $this->reload()`: reload the current page (with context).
- `return $this->refresh(1)`*: refresh only the instance with an id on `1`. We can pass an id array also to refresh more than one instance.
- `return $this->view('view.name', ['some'=>'params'])`: display a view right in Sharp; useful for page previews.
Expand Down
8 changes: 6 additions & 2 deletions resources/js/commands/CommandManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ export class CommandManager {

get defaultCommandResponseHandlers(): CommandResponseHandlers {
return {
info: async ({ message }, { formModal }) => {
info: async ({ message, reload }, { formModal }) => {
await showAlert(message, {
title: __('sharp::modals.command.info.title'),
});
formModal.shouldReopen && formModal.reloadAndReopen();
if(formModal.shouldReopen) {
formModal.reloadAndReopen();
} else if(reload) {
await this.handleCommandResponse({ action: 'reload' });
}
},
link: ({ link }, { formModal }) => {
if(formModal.shouldReopen) {
Expand Down
10 changes: 5 additions & 5 deletions resources/js/types/generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export type CommandFormData = {
};
export type CommandResponseData =
| { action: "link"; link: string }
| { action: "info"; message: string }
| { action: "info"; message: string; reload: boolean }
| { action: "refresh"; items?: Array<{ [key: string]: any }> }
| { action: "reload" }
| { action: "step"; step: string }
Expand Down Expand Up @@ -601,13 +601,13 @@ export type FormUploadFieldValueData = {
} | null;
nativeFile?: File;
};
export type GlobalSearchData = {
config: { placeholder: string };
};
export type GlobalFiltersData = {
config: { filters: ConfigFiltersData };
filterValues: FilterValuesData;
};
export type GlobalSearchData = {
config: { placeholder: string };
};
export type GraphWidgetData = {
value?: {
key: string;
Expand Down Expand Up @@ -830,8 +830,8 @@ export type ShowListFieldData = {
key: string;
type: "list";
emptyVisible: boolean;
label: string | null;
itemFields: { [key: string]: ShowFieldData };
label: string | null;
};
export type ShowPictureFieldData = {
value?: string;
Expand Down
1 change: 1 addition & 0 deletions resources/js/types/routes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ declare module 'ziggy-js' {
"name": "filterKey"
}
],
"code16.sharp.update-assets": [],
"code16.sharp.api.dashboard.command.form": [
{
"name": "dashboardKey"
Expand Down
2 changes: 1 addition & 1 deletion src/Data/Commands/CommandResponseData.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// download & streamDownload actions returns the file directly in the response
#[LiteralTypeScriptType(
'{ action: "'.CommandAction::Link->value.'", link: string } | '.
'{ action: "'.CommandAction::Info->value.'", message: string } | '.
'{ action: "'.CommandAction::Info->value.'", message: string, reload: boolean } | '.
'{ action: "'.CommandAction::Refresh->value.'", items?: Array<{ [key: string]: any }> } | '.
'{ action: "'.CommandAction::Reload->value.'" } | '.
'{ action: "'.CommandAction::Step->value.'", step: string } | '.
Expand Down
3 changes: 2 additions & 1 deletion src/EntityList/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ abstract class Command
private ?string $confirmationButtonLabel = null;
private ?string $description = null;

protected function info(string $message): array
protected function info(string $message, bool $reload = false): array
{
return [
'action' => CommandAction::Info->value,
'message' => $message,
'reload' => $reload,
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/EntityList/Commands/EntityState.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected function view(string $bladeView, array $params = []): array
throw new SharpInvalidConfigException('View return type is not supported for a state.');
}

protected function info(string $message): array
protected function info(string $message, bool $reload = false): array
{
throw new SharpInvalidConfigException('Info return type is not supported for a state.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function execute(array $data = []): array
->assertJson([
'action' => 'info',
'message' => 'ok',
'reload' => false,
]);
});

Expand Down Expand Up @@ -74,6 +75,37 @@ public function execute(array $data = []): array
]);
});

it('allows to call an info + reload entity command', function () {
fakeListFor('person', new class() extends PersonList
{
protected function getEntityCommands(): ?array
{
return [
'cmd' => new class() extends EntityCommand
{
public function label(): ?string
{
return 'entity';
}

public function execute(array $data = []): array
{
return $this->info('ok', reload: true);
}
},
];
}
});

$this->postJson(route('code16.sharp.api.list.command.entity', ['person', 'cmd']))
->assertOk()
->assertJson([
'action' => 'info',
'message' => 'ok',
'reload' => true,
]);
});

it('allows to call a view entity command', function () {
fakeListFor('person', new class() extends PersonList
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function execute($instanceId, array $data = []): array
->assertJson([
'action' => 'info',
'message' => 'ok',
'reload' => false,
]);
});

Expand Down Expand Up @@ -73,6 +74,37 @@ public function execute($instanceId, array $data = []): array
]);
});

it('allows to call an info + reload instance command', function () {
fakeListFor('person', new class() extends PersonList
{
protected function getInstanceCommands(): ?array
{
return [
'instance_info' => new class() extends InstanceCommand
{
public function label(): ?string
{
return 'my command';
}

public function execute($instanceId, array $data = []): array
{
return $this->info('ok', reload: true);
}
},
];
}
});

$this->postJson(route('code16.sharp.api.list.command.instance', ['person', 'instance_info', 1]))
->assertOk()
->assertJson([
'action' => 'info',
'message' => 'ok',
'reload' => true,
]);
});

it('allows to call a view instance command', function () {
fakeListFor('person', new class() extends PersonList
{
Expand Down
Loading