impl settings page
This commit is contained in:
parent
35dea402ab
commit
08e12cd218
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\CheckinWebhook;
|
||||||
use App\DeactivatedUser;
|
use App\DeactivatedUser;
|
||||||
use App\Ejaculation;
|
use App\Ejaculation;
|
||||||
use App\Exceptions\CsvImportException;
|
use App\Exceptions\CsvImportException;
|
||||||
@ -75,6 +76,31 @@ class SettingController extends Controller
|
|||||||
return redirect()->route('setting.privacy')->with('status', 'プライバシー設定を更新しました。');
|
return redirect()->route('setting.privacy')->with('status', 'プライバシー設定を更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function webhooks()
|
||||||
|
{
|
||||||
|
$webhooks = Auth::user()->checkinWebhooks;
|
||||||
|
|
||||||
|
return view('setting.webhooks')->with(compact('webhooks'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeWebhooks(Request $request)
|
||||||
|
{
|
||||||
|
$validated = $request->validate([
|
||||||
|
'name' => 'required|string|max:255'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Auth::user()->checkinWebhooks()->create($validated);
|
||||||
|
|
||||||
|
return redirect()->route('setting.webhooks')->with('status', '作成しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroyWebhooks(CheckinWebhook $webhook)
|
||||||
|
{
|
||||||
|
$webhook->delete();
|
||||||
|
|
||||||
|
return redirect()->route('setting.webhooks')->with('status', '削除しました。');
|
||||||
|
}
|
||||||
|
|
||||||
public function import()
|
public function import()
|
||||||
{
|
{
|
||||||
return view('setting.import');
|
return view('setting.import');
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"@types/bootstrap": "^4.5.0",
|
"@types/bootstrap": "^4.5.0",
|
||||||
"@types/cal-heatmap": "^3.3.10",
|
"@types/cal-heatmap": "^3.3.10",
|
||||||
"@types/chart.js": "^2.9.22",
|
"@types/chart.js": "^2.9.22",
|
||||||
|
"@types/clipboard": "^2.0.1",
|
||||||
"@types/jquery": "^3.3.38",
|
"@types/jquery": "^3.3.38",
|
||||||
"@types/js-cookie": "^2.2.0",
|
"@types/js-cookie": "^2.2.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.1.0",
|
"@typescript-eslint/eslint-plugin": "^3.1.0",
|
||||||
@ -23,6 +24,7 @@
|
|||||||
"bootstrap": "^4.5.0",
|
"bootstrap": "^4.5.0",
|
||||||
"cal-heatmap": "^3.3.10",
|
"cal-heatmap": "^3.3.10",
|
||||||
"chart.js": "^2.7.1",
|
"chart.js": "^2.7.1",
|
||||||
|
"clipboard": "^2.0.6",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"date-fns": "^1.30.1",
|
"date-fns": "^1.30.1",
|
||||||
"eslint": "^7.2.0",
|
"eslint": "^7.2.0",
|
||||||
|
29
resources/assets/js/setting/webhooks.ts
Normal file
29
resources/assets/js/setting/webhooks.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as ClipboardJS from 'clipboard';
|
||||||
|
|
||||||
|
$('.webhook-url').on('focus', function () {
|
||||||
|
$(this).trigger('select');
|
||||||
|
});
|
||||||
|
|
||||||
|
new ClipboardJS('.copy-to-clipboard', {
|
||||||
|
target(elem: Element): Element {
|
||||||
|
return elem.parentElement?.parentElement?.querySelector('.webhook-url') as Element;
|
||||||
|
},
|
||||||
|
}).on('success', (e) => {
|
||||||
|
e.clearSelection();
|
||||||
|
$(e.trigger).popover('show');
|
||||||
|
});
|
||||||
|
$('.copy-to-clipboard').on('shown.bs.popover', function () {
|
||||||
|
setTimeout(() => $(this).popover('hide'), 3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
const $deleteModal = $('#deleteIncomingWebhookModal');
|
||||||
|
$deleteModal.find('.btn-danger').on('click', function () {
|
||||||
|
const $form = $deleteModal.find('form');
|
||||||
|
$form.attr('action', $form.attr('action')?.replace('@', $deleteModal.data('id')) || null);
|
||||||
|
$form.submit();
|
||||||
|
});
|
||||||
|
$('[data-target="#deleteIncomingWebhookModal"]').on('click', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$deleteModal.data('id', $(this).data('id'));
|
||||||
|
$deleteModal.modal('show', this);
|
||||||
|
});
|
@ -10,6 +10,8 @@
|
|||||||
href="{{ route('setting') }}"><span class="oi oi-person mr-1"></span> プロフィール</a>
|
href="{{ route('setting') }}"><span class="oi oi-person mr-1"></span> プロフィール</a>
|
||||||
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.privacy' ? 'active' : '' }}"
|
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.privacy' ? 'active' : '' }}"
|
||||||
href="{{ route('setting.privacy') }}"><span class="oi oi-shield mr-1"></span> プライバシー</a>
|
href="{{ route('setting.privacy') }}"><span class="oi oi-shield mr-1"></span> プライバシー</a>
|
||||||
|
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.webhooks' ? 'active' : '' }}"
|
||||||
|
href="{{ route('setting.webhooks') }}"><span class="oi oi-link-intact mr-1"></span> Webhook</a>
|
||||||
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.import' ? 'active' : '' }}"
|
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.import' ? 'active' : '' }}"
|
||||||
href="{{ route('setting.import') }}"><span class="oi oi-data-transfer-upload mr-1"></span> データのインポート</a>
|
href="{{ route('setting.import') }}"><span class="oi oi-data-transfer-upload mr-1"></span> データのインポート</a>
|
||||||
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.export' ? 'active' : '' }}"
|
<a class="list-group-item list-group-item-action {{ Route::currentRouteName() === 'setting.export' ? 'active' : '' }}"
|
||||||
|
63
resources/views/setting/webhooks.blade.php
Normal file
63
resources/views/setting/webhooks.blade.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
@extends('setting.base')
|
||||||
|
|
||||||
|
@section('title', 'Webhook')
|
||||||
|
|
||||||
|
@section('tab-content')
|
||||||
|
<h3>Incoming Webhook</h3>
|
||||||
|
<hr>
|
||||||
|
<p>さまざまなシステムと連携してチェックインを行うためのWebhook URLを作成することができます。APIドキュメントは<a href="{{ url('/apidoc.html') }}">こちら</a>から参照いただけます。</p>
|
||||||
|
<h4>新規作成</h4>
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="font-weight-bold">おことわり</h6>
|
||||||
|
<p>Webhook APIは予告なく仕様変更を行う場合がございます。また、サーバに対する過剰なリクエストや、不審な公開チェックインを繰り返している場合には管理者の裁量によって予告なく無効化(削除)する場合があります。</p>
|
||||||
|
<p>通常利用と同様、1分以内のチェックインは禁止されていることを考慮してください。また、テスト目的であれば非公開チェックインをご活用ください。</p>
|
||||||
|
<hr>
|
||||||
|
<form action="{{ route('setting.webhooks.store') }}" method="post">
|
||||||
|
{{ csrf_field() }}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">名前 (メモ)</label>
|
||||||
|
<input id="name" class="form-control" name="name" type="text" required>
|
||||||
|
<small class="form-text text-muted">後で分かるように名前を付けておいてください。</small>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">新規作成</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (!empty($webhooks))
|
||||||
|
<h4 class="mt-4">作成済みのWebhook</h4>
|
||||||
|
<div class="list-group mt-3">
|
||||||
|
@foreach ($webhooks as $webhook)
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<div class="flex-grow-1 mr-2">
|
||||||
|
<div>{{ $webhook->name }}</div>
|
||||||
|
<input class="webhook-url form-control form-control-sm mt-1" type="text" value="{{ url('/api/webhooks/' . $webhook->id) }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div class="ml-2">
|
||||||
|
<button class="btn btn-outline-secondary copy-to-clipboard" type="button" data-toggle="popover" data-trigger="manual" data-placement="top" data-content="コピーしました!">コピー</button>
|
||||||
|
<button class="btn btn-outline-danger" type="button" data-target="#deleteIncomingWebhookModal" data-id="{{ $webhook->id }}">削除</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@component('components.modal', ['id' => 'deleteIncomingWebhookModal'])
|
||||||
|
@slot('title')
|
||||||
|
削除確認
|
||||||
|
@endslot
|
||||||
|
Webhookを削除してもよろしいですか?
|
||||||
|
<form action="{{ route('setting.webhooks.destroy', ['webhook' => '@']) }}" method="post">
|
||||||
|
{{ csrf_field() }}
|
||||||
|
{{ method_field('DELETE') }}
|
||||||
|
</form>
|
||||||
|
@slot('footer')
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button>
|
||||||
|
<button type="button" class="btn btn-danger">削除</button>
|
||||||
|
@endslot
|
||||||
|
@endcomponent
|
||||||
|
|
||||||
|
@push('script')
|
||||||
|
<script src="{{ mix('js/setting/webhooks.js') }}"></script>
|
||||||
|
@endpush
|
@ -39,6 +39,9 @@ Route::middleware('auth')->group(function () {
|
|||||||
Route::post('/setting/profile', 'SettingController@updateProfile')->name('setting.profile.update');
|
Route::post('/setting/profile', 'SettingController@updateProfile')->name('setting.profile.update');
|
||||||
Route::get('/setting/privacy', 'SettingController@privacy')->name('setting.privacy');
|
Route::get('/setting/privacy', 'SettingController@privacy')->name('setting.privacy');
|
||||||
Route::post('/setting/privacy', 'SettingController@updatePrivacy')->name('setting.privacy.update');
|
Route::post('/setting/privacy', 'SettingController@updatePrivacy')->name('setting.privacy.update');
|
||||||
|
Route::get('/setting/webhooks', 'SettingController@webhooks')->name('setting.webhooks');
|
||||||
|
Route::post('/setting/webhooks', 'SettingController@storeWebhooks')->name('setting.webhooks.store');
|
||||||
|
Route::delete('/setting/webhooks/{webhook}', 'SettingController@destroyWebhooks')->name('setting.webhooks.destroy');
|
||||||
Route::get('/setting/import', 'SettingController@import')->name('setting.import');
|
Route::get('/setting/import', 'SettingController@import')->name('setting.import');
|
||||||
Route::post('/setting/import', 'SettingController@storeImport')->name('setting.import');
|
Route::post('/setting/import', 'SettingController@storeImport')->name('setting.import');
|
||||||
Route::delete('/setting/import', 'SettingController@destroyImport')->name('setting.import.destroy');
|
Route::delete('/setting/import', 'SettingController@destroyImport')->name('setting.import.destroy');
|
||||||
|
12
webpack.mix.js
vendored
12
webpack.mix.js
vendored
@ -1,5 +1,6 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const mix = require('laravel-mix');
|
const mix = require('laravel-mix');
|
||||||
require('laravel-mix-bundle-analyzer')
|
require('laravel-mix-bundle-analyzer');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -19,18 +20,19 @@ mix.ts('resources/assets/js/app.ts', 'public/js')
|
|||||||
.ts('resources/assets/js/setting/privacy.ts', 'public/js/setting')
|
.ts('resources/assets/js/setting/privacy.ts', 'public/js/setting')
|
||||||
.ts('resources/assets/js/setting/import.ts', 'public/js/setting')
|
.ts('resources/assets/js/setting/import.ts', 'public/js/setting')
|
||||||
.ts('resources/assets/js/setting/deactivate.ts', 'public/js/setting')
|
.ts('resources/assets/js/setting/deactivate.ts', 'public/js/setting')
|
||||||
|
.ts('resources/assets/js/setting/webhooks.ts', 'public/js/setting')
|
||||||
.ts('resources/assets/js/checkin.ts', 'public/js')
|
.ts('resources/assets/js/checkin.ts', 'public/js')
|
||||||
.sass('resources/assets/sass/app.scss', 'public/css')
|
.sass('resources/assets/sass/app.scss', 'public/css')
|
||||||
.autoload({
|
.autoload({
|
||||||
'jquery': ['$', 'jQuery', 'window.jQuery']
|
jquery: ['$', 'jQuery', 'window.jQuery'],
|
||||||
})
|
})
|
||||||
.extract(['jquery', 'bootstrap'])
|
.extract(['jquery', 'bootstrap'])
|
||||||
.extract(['chart.js', 'chartjs-color', 'color-name', 'moment', 'cal-heatmap', 'd3'], 'public/js/vendor/chart')
|
.extract(['chart.js', 'chartjs-color', 'color-name', 'moment', 'cal-heatmap', 'd3'], 'public/js/vendor/chart')
|
||||||
.version()
|
.version()
|
||||||
.webpackConfig(webpack => ({
|
.webpackConfig((_webpack) => ({
|
||||||
externals: {
|
externals: {
|
||||||
moment: 'moment'
|
moment: 'moment',
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (process.argv.includes('-a')) {
|
if (process.argv.includes('-a')) {
|
||||||
|
@ -917,6 +917,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
moment "^2.10.2"
|
moment "^2.10.2"
|
||||||
|
|
||||||
|
"@types/clipboard@^2.0.1":
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/clipboard/-/clipboard-2.0.1.tgz#75a74086c293d75b12bc93ff13bc7797fef05a40"
|
||||||
|
integrity sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==
|
||||||
|
|
||||||
"@types/color-name@^1.1.1":
|
"@types/color-name@^1.1.1":
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||||
@ -2199,7 +2204,7 @@ cli-width@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
|
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
|
||||||
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
|
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
|
||||||
|
|
||||||
clipboard@^2.0.0:
|
clipboard@^2.0.0, clipboard@^2.0.6:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
|
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
|
||||||
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
|
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
|
||||||
|
Loading…
Reference in New Issue
Block a user