diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php index 45281b9..b3a46ba 100644 --- a/app/Http/Controllers/SettingController.php +++ b/app/Http/Controllers/SettingController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\CheckinWebhook; use App\DeactivatedUser; use App\Ejaculation; use App\Exceptions\CsvImportException; @@ -75,6 +76,31 @@ class SettingController extends Controller 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() { return view('setting.import'); diff --git a/package.json b/package.json index a029a4c..6f73fba 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@types/bootstrap": "^4.5.0", "@types/cal-heatmap": "^3.3.10", "@types/chart.js": "^2.9.22", + "@types/clipboard": "^2.0.1", "@types/jquery": "^3.3.38", "@types/js-cookie": "^2.2.0", "@typescript-eslint/eslint-plugin": "^3.1.0", @@ -23,6 +24,7 @@ "bootstrap": "^4.5.0", "cal-heatmap": "^3.3.10", "chart.js": "^2.7.1", + "clipboard": "^2.0.6", "cross-env": "^5.2.0", "date-fns": "^1.30.1", "eslint": "^7.2.0", diff --git a/resources/assets/js/setting/webhooks.ts b/resources/assets/js/setting/webhooks.ts new file mode 100644 index 0000000..42933d2 --- /dev/null +++ b/resources/assets/js/setting/webhooks.ts @@ -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); +}); diff --git a/resources/views/setting/base.blade.php b/resources/views/setting/base.blade.php index 5ed6441..83fe82e 100644 --- a/resources/views/setting/base.blade.php +++ b/resources/views/setting/base.blade.php @@ -10,6 +10,8 @@ href="{{ route('setting') }}"> プロフィール プライバシー + Webhook データのインポート Incoming Webhook +
+

さまざまなシステムと連携してチェックインを行うためのWebhook URLを作成することができます。APIドキュメントはこちらから参照いただけます。

+

新規作成

+
+
+
おことわり
+

Webhook APIは予告なく仕様変更を行う場合がございます。また、サーバに対する過剰なリクエストや、不審な公開チェックインを繰り返している場合には管理者の裁量によって予告なく無効化(削除)する場合があります。

+

通常利用と同様、1分以内のチェックインは禁止されていることを考慮してください。また、テスト目的であれば非公開チェックインをご活用ください。

+
+
+ {{ csrf_field() }} +
+ + + 後で分かるように名前を付けておいてください。 +
+ +
+
+
+ @if (!empty($webhooks)) +

作成済みのWebhook

+
+ @foreach ($webhooks as $webhook) +
+
+
{{ $webhook->name }}
+ +
+
+ + +
+
+ @endforeach +
+ @endif +@endsection + +@component('components.modal', ['id' => 'deleteIncomingWebhookModal']) + @slot('title') + 削除確認 + @endslot + Webhookを削除してもよろしいですか? +
+ {{ csrf_field() }} + {{ method_field('DELETE') }} +
+ @slot('footer') + + + @endslot +@endcomponent + +@push('script') + +@endpush diff --git a/routes/web.php b/routes/web.php index 0a9b8f9..c545c2a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -39,6 +39,9 @@ Route::middleware('auth')->group(function () { Route::post('/setting/profile', 'SettingController@updateProfile')->name('setting.profile.update'); Route::get('/setting/privacy', 'SettingController@privacy')->name('setting.privacy'); 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::post('/setting/import', 'SettingController@storeImport')->name('setting.import'); Route::delete('/setting/import', 'SettingController@destroyImport')->name('setting.import.destroy'); diff --git a/webpack.mix.js b/webpack.mix.js index 6fe6635..b19cc2e 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -1,5 +1,6 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires 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/import.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') .sass('resources/assets/sass/app.scss', 'public/css') .autoload({ - 'jquery': ['$', 'jQuery', 'window.jQuery'] + jquery: ['$', 'jQuery', 'window.jQuery'], }) .extract(['jquery', 'bootstrap']) .extract(['chart.js', 'chartjs-color', 'color-name', 'moment', 'cal-heatmap', 'd3'], 'public/js/vendor/chart') .version() - .webpackConfig(webpack => ({ + .webpackConfig((_webpack) => ({ externals: { - moment: 'moment' - } + moment: 'moment', + }, })); if (process.argv.includes('-a')) { diff --git a/yarn.lock b/yarn.lock index 815fd39..b145131 100644 --- a/yarn.lock +++ b/yarn.lock @@ -917,6 +917,11 @@ dependencies: 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": version "1.1.1" 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" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== -clipboard@^2.0.0: +clipboard@^2.0.0, clipboard@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==