Skip to content

Commit

Permalink
Preferences UI (statamic#7226)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesse Leite <[email protected]>
Co-authored-by: Jack McDade <[email protected]>
Co-authored-by: StyleCI Bot <[email protected]>
  • Loading branch information
4 people committed Jan 13, 2023
1 parent 63e3755 commit 62c6fed
Show file tree
Hide file tree
Showing 20 changed files with 876 additions and 11 deletions.
1 change: 1 addition & 0 deletions resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Statamic.app({
NavigationListing: require('./components/navigation/Listing.vue').default,
NavigationCreateForm: require('./components/navigation/CreateForm.vue').default,
NavigationEditForm: require('./components/navigation/EditForm.vue').default,
PreferencesEditForm: require('./components/preferences/EditForm.vue').default,
NavigationView: require('./components/navigation/View.vue').default,
TaxonomyCreateForm: require('./components/taxonomies/CreateForm.vue').default,
TaxonomyEditForm: require('./components/taxonomies/EditForm.vue').default,
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/nav/Builder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<dropdown-list v-if="hasSaveAsOptions" class="ml-0">
<template #trigger>
<button class="btn-primary rounded-l-none flex items-center" :class="{ 'disabled': !changed }">
<button class="btn-primary rounded-l-none flex items-center">
<svg-icon name="chevron-down-xs" class="w-2" />
</button>
</template>
Expand Down
150 changes: 150 additions & 0 deletions resources/js/components/preferences/EditForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<template>

<publish-container
ref="container"
:name="name"
:blueprint="blueprint"
v-model="currentValues"
reference="collection"
:meta="meta"
:errors="errors"
v-slot="{ setFieldValue, setFieldMeta }"
>
<div>
<breadcrumbs v-if="breadcrumbs" :crumbs="breadcrumbs" />

<div class="flex items-center mb-3">
<h1 class="flex-1">{{ title }}</h1>

<div class="ml-2 text-left" :class="{ 'btn-group': hasSaveAsOptions }">
<button
class="btn-primary pl-2"
:class="{ 'disabled': !isDirty }"
:disabled="!isDirty"
@click="save"
v-text="__('Save')" />

<dropdown-list v-if="hasSaveAsOptions" class="ml-0">
<template #trigger>
<button class="btn-primary rounded-l-none flex items-center">
<svg-icon name="chevron-down-xs" class="w-2" />
</button>
</template>
<h6 class="p-1">{{ __('Save to') }}...</h6>
<dropdown-item v-for="option in saveAsOptions" :key="option.url" @click="saveAs(option.url)">
<div class="flex items-start pr-2">
<svg-icon :name="option.icon" class="text-grey flex-shrink-0 mr-1 w-4 group-hover:text-white" />
<span class="whitespace-normal">{{ option.label }}</span>
</div>
</dropdown-item>
</dropdown-list>
</div>
</div>

<publish-sections
@updated="setFieldValue"
@meta-updated="setFieldMeta"
:can-toggle-labels="canToggleLabels"
:enable-sidebar="hasSidebar"
:read-only="readOnly" />
</div>
</publish-container>

</template>

<script>
export default {
props: {
blueprint: { required: true, type: Object },
meta: { required: true, type: Object },
values: { required: true, type: Object },
title: { required: true, type: String },
name: { type: String, default: 'base' },
breadcrumbs: Array,
action: String,
canToggleLabels: { type: Boolean, default: true },
readOnly: { type: Boolean, default: false },
reloadOnSave: { type: Boolean, default: false },
saveAsOptions: { type: Array, default: () => [] },
},
data() {
return {
saving: false,
currentValues: this.values,
error: null,
errors: {},
hasSidebar: this.blueprint.sections.map(section => section.handle).includes('sidebar'),
}
},
computed: {
hasSaveAsOptions() {
return this.saveAsOptions.length;
},
isDirty() {
return this.$dirty.has(this.name);
}
},
methods: {
clearErrors() {
this.error = null;
this.errors = {};
},
save() {
this.saveAs(this.action);
},
saveAs(url) {
this.saving = true;
this.clearErrors();
this.$axios
.patch(url, this.currentValues)
.then(() => {
this.$refs.container.saved();
location.reload();
})
.catch(e => this.handleAxiosError(e));
},
handleAxiosError(e) {
this.saving = false;
if (e.response && e.response.status === 422) {
const { message, errors } = e.response.data;
this.error = message;
this.errors = errors;
this.$toast.error(message);
} else {
const message = data_get(e, 'response.data.message');
this.$toast.error(message || e);
console.log(e);
}
},
},
created() {
this.$keys.bindGlobal(['mod+s'], e => {
e.preventDefault();
this.save();
});
},
watch: {
saving(saving) {
this.$progress.loading('preferences-edit-form', saving);
}
},
}
</script>
3 changes: 3 additions & 0 deletions resources/lang/en/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@
'password_protect_token_invalid' => 'Invalid or expired token.',
'password_protect_token_missing' => 'Secure token is missing. You must arrive at this screen from the original, protected URL.',
'phpinfo_utility_description' => 'Check PHP configuration settings and installed modules.',
'preference_favorites_instructions' => 'Shortcuts that will be shown when opening the global search bar. You may alternatively visit the page and use the pin icon at the top to add it to this list.',
'preference_locale_instructions' => 'The preferred language for the control panel.',
'preference_start_page_instructions' => 'The page to be should be shown when logging into the control panel.',
'publish_actions_create_revision' => 'A revision will be created based off the working copy. The current revision will not change.',
'publish_actions_current_becomes_draft_because_scheduled' => 'Since the current revision is published and you\'ve selected a date in the future, once you submit, the revision will act like a draft until the selected date.',
'publish_actions_publish' => 'Changes to the working copy will applied to the entry and it will be published immediately.',
Expand Down
1 change: 1 addition & 0 deletions resources/views/partials/global-header.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
@cp_svg('cog')
</button>
</template>
<dropdown-item :text="__('Preferences')" redirect="{{ route('statamic.cp.preferences.index') }}"></dropdown-item>
<dropdown-item :text="__('CP Nav')" redirect="{{ route('statamic.cp.preferences.nav.index') }}"></dropdown-item>
</dropdown-list>

Expand Down
22 changes: 22 additions & 0 deletions resources/views/preferences/edit.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@extends('statamic::layout')
@section('title', $title)

@section('content')

@if($showBreadcrumb)
@include('statamic::partials.breadcrumb', [
'url' => cp_route('preferences.index'),
'title' => __('Preferences'),
])
@endif

<preferences-edit-form
title="{{ $title }}"
:blueprint='@json($blueprint)'
:meta='@json($meta)'
:values='@json($values)'
action="{{ $actionUrl }}"
:save-as-options="{{ json_encode($saveAsOptions) }}"
></preferences-edit-form>

@stop
74 changes: 74 additions & 0 deletions resources/views/preferences/index.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@extends('statamic::layout')
@section('title', __('Preferences'))

@section('content')

<div class="flex justify-between items-center mb-3">
<h1>@yield('title')</h1>
</div>

<div class="card p-0 mb-2">
<table class="data-table">
<tr>
<td>
<div class="flex items-center">
<div class="w-4 h-4 mr-2">@cp_svg('earth')</div>
<a href="{{ cp_route('preferences.default.edit') }}">{{ __('Default') }}</a>
</div>
</td>
<td class="text-right text-2xs text-grey-50">
@if (!empty(Statamic\Facades\Preference::default()->all()))
{{ __('Modified') }}
@endif
</td>
</tr>
</table>
</div>

@if (Statamic\Facades\Role::all()->isNotEmpty())
<h3 class="little-heading pl-0 mb-1">{{ __('Override For Role') }}</h3>
<div class="card p-0 mb-2">
<table class="data-table">
@foreach (Statamic\Facades\Role::all() as $role)
<tr>
<td>
<div class="flex items-center">
<div class="w-4 h-4 mr-2">@cp_svg('shield-key')</div>
<a href="{{ cp_route('preferences.role.edit', [$role->handle()]) }}">{{ __($role->title()) }}</a>
</div>
</td>
<td class="text-right text-2xs text-grey-50">
@if (!empty($role->preferences()))
{{ __('Modified') }}
@endif
</td>
</tr>
@endforeach
</table>
</div>
@endif

<h3 class="little-heading pl-0 mb-1">{{ __('Override For User') }}</h3>
<div class="card p-0 mb-2">
<table class="data-table">
<tr>
<td>
<div class="flex items-center">
<div class="w-4 h-4 mr-2">@cp_svg('user')</div>
<a href="{{ cp_route('preferences.user.edit') }}">{{ __('My Preferences') }}</a>
</div>
</td>
<td class="text-right text-2xs text-grey-50">
@if (!empty(auth()->user()->preferences()))
{{ __('Modified') }}
@endif
</td>
</tr>
</table>
</div>

@include('statamic::partials.docs-callout', [
'topic' => __('Preferences'),
'url' => Statamic::docsUrl('preferences')
])
@endsection
11 changes: 11 additions & 0 deletions routes/cp.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,17 @@
});

Route::group(['prefix' => 'preferences', 'as' => 'preferences.', 'namespace' => 'Preferences'], function () {
Route::get('/', 'PreferenceController@index')->name('index');
Route::get('edit', 'UserPreferenceController@edit')->name('user.edit');
Route::patch('/', 'UserPreferenceController@update')->name('user.update');

Route::middleware([RequireStatamicPro::class, 'can:manage preferences'])->group(function () {
Route::get('roles/{role}/edit', 'RolePreferenceController@edit')->name('role.edit');
Route::patch('roles/{role}', 'RolePreferenceController@update')->name('role.update');
Route::get('default/edit', 'DefaultPreferenceController@edit')->name('default.edit');
Route::patch('default', 'DefaultPreferenceController@update')->name('default.update');
});

Route::post('js', 'PreferenceController@store')->name('store');
Route::delete('js/{key}', 'PreferenceController@destroy')->name('destroy');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Statamic\Http\Controllers\CP\Preferences;

use Illuminate\Http\Request;
use Statamic\Facades\Preference;
use Statamic\Http\Controllers\CP\CpController;

class DefaultPreferenceController extends CpController
{
use ManagesPreferences;

public function edit()
{
return $this->view(
__('Default'),
cp_route('preferences.default.update'),
Preference::default()->all()
);
}

public function update(Request $request)
{
return $this->updatePreferences($request, Preference::default());
}

private function ignoreSaveAsOption()
{
return 'default';
}
}
Loading

0 comments on commit 62c6fed

Please sign in to comment.