Apply previous changes: slack webhooks, date range issue, 12h am/pm format, today preifl, api-keys
This commit is contained in:
parent
341ee04268
commit
18ed28da2e
|
@ -40,6 +40,7 @@ abstract class UserFormRequest extends \Illuminate\Foundation\Http\FormRequest
|
||||||
'notifications_include_submission' => 'boolean',
|
'notifications_include_submission' => 'boolean',
|
||||||
'webhook_url' => 'url|nullable',
|
'webhook_url' => 'url|nullable',
|
||||||
'use_captcha' => 'boolean',
|
'use_captcha' => 'boolean',
|
||||||
|
'slack_webhook_url' => 'url|nullable',
|
||||||
|
|
||||||
// Customization
|
// Customization
|
||||||
'theme' => ['required',Rule::in(Form::THEMES)],
|
'theme' => ['required',Rule::in(Form::THEMES)],
|
||||||
|
@ -96,7 +97,9 @@ abstract class UserFormRequest extends \Illuminate\Foundation\Http\FormRequest
|
||||||
|
|
||||||
// Date field
|
// Date field
|
||||||
'properties.*.with_time' => 'boolean|nullable',
|
'properties.*.with_time' => 'boolean|nullable',
|
||||||
|
'properties.*.use_am_pm' => 'boolean|nullable',
|
||||||
'properties.*.date_range' => 'boolean|nullable',
|
'properties.*.date_range' => 'boolean|nullable',
|
||||||
|
'properties.*.prefill_today' => 'boolean|nullable',
|
||||||
|
|
||||||
// Select / Multi Select field
|
// Select / Multi Select field
|
||||||
'properties.*.allow_creation' => 'boolean|nullable',
|
'properties.*.allow_creation' => 'boolean|nullable',
|
||||||
|
|
|
@ -29,6 +29,7 @@ class FormResource extends JsonResource
|
||||||
'views_count' => $this->when($this->workspace->is_pro, $this->views_count),
|
'views_count' => $this->when($this->workspace->is_pro, $this->views_count),
|
||||||
'submissions_count' => $this->when($this->workspace->is_pro, $this->submissions_count),
|
'submissions_count' => $this->when($this->workspace->is_pro, $this->submissions_count),
|
||||||
'notifies' => $this->notifies,
|
'notifies' => $this->notifies,
|
||||||
|
'notifies_slack' => $this->notifies_slack,
|
||||||
'send_submission_confirmation' => $this->send_submission_confirmation,
|
'send_submission_confirmation' => $this->send_submission_confirmation,
|
||||||
'webhook_url' => $this->webhook_url,
|
'webhook_url' => $this->webhook_url,
|
||||||
'redirect_url' => $this->redirect_url,
|
'redirect_url' => $this->redirect_url,
|
||||||
|
@ -42,6 +43,7 @@ class FormResource extends JsonResource
|
||||||
'password' => $this->password,
|
'password' => $this->password,
|
||||||
'tags' => $this->tags,
|
'tags' => $this->tags,
|
||||||
'notification_emails' => $this->notification_emails,
|
'notification_emails' => $this->notification_emails,
|
||||||
|
'slack_webhook_url' => $this->slack_webhook_url,
|
||||||
] : [];
|
] : [];
|
||||||
|
|
||||||
$baseData = $this->getFilteredFormData(parent::toArray($request), $userIsFormOwner);
|
$baseData = $this->getFilteredFormData(parent::toArray($request), $userIsFormOwner);
|
||||||
|
|
|
@ -7,6 +7,8 @@ use App\Notifications\Forms\FormSubmissionNotification;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Spatie\WebhookServer\WebhookCall;
|
||||||
|
use App\Service\Forms\FormSubmissionFormatter;
|
||||||
|
|
||||||
class NotifyFormSubmission implements ShouldQueue
|
class NotifyFormSubmission implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -20,18 +22,77 @@ class NotifyFormSubmission implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle(FormSubmitted $event)
|
public function handle(FormSubmitted $event)
|
||||||
{
|
{
|
||||||
if (!$event->form->notifies || !$event->form->is_pro) return;
|
if (!$event->form->is_pro) return;
|
||||||
|
|
||||||
$subscribers = collect(preg_split("/\r\n|\n|\r/", $event->form->notification_emails))->filter(function($email) {
|
if ($event->form->notifies) {
|
||||||
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
// Send Email Notification
|
||||||
});
|
$subscribers = collect(preg_split("/\r\n|\n|\r/", $event->form->notification_emails))->filter(function($email) {
|
||||||
\Log::debug('Sending email notification',[
|
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||||
'recipients' => $subscribers->toArray(),
|
});
|
||||||
'form_id' => $event->form->id,
|
\Log::debug('Sending email notification',[
|
||||||
'form_slug' => $event->form->slug,
|
'recipients' => $subscribers->toArray(),
|
||||||
]);
|
'form_id' => $event->form->id,
|
||||||
$subscribers->each(function ($subscriber) use ($event) {
|
'form_slug' => $event->form->slug,
|
||||||
Notification::route('mail', $subscriber)->notify(new FormSubmissionNotification($event));
|
]);
|
||||||
});
|
$subscribers->each(function ($subscriber) use ($event) {
|
||||||
|
Notification::route('mail', $subscriber)->notify(new FormSubmissionNotification($event));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event->form->notifies_slack) {
|
||||||
|
// Send Slack Notification
|
||||||
|
$this->sendSlackNotification($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendSlackNotification(FormSubmitted $event)
|
||||||
|
{
|
||||||
|
if($this->validateSlackWebhookUrl($event->form->slack_webhook_url)){
|
||||||
|
$submissionString = "";
|
||||||
|
$formatter = (new FormSubmissionFormatter($event->form, $event->data))->outputStringsOnly();
|
||||||
|
foreach ($formatter->getFieldsWithValue() as $field) {
|
||||||
|
$tmpVal = is_array($field['value']) ? implode(",", $field['value']) : $field['value'];
|
||||||
|
$submissionString .= ">*".ucfirst($field['name'])."*: ".$tmpVal." \n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$formURL = url("forms/".$event->form->slug);
|
||||||
|
$editFormURL = url("forms/".$event->form->slug."/show");
|
||||||
|
$finalSlackPostData = [
|
||||||
|
'blocks' => [
|
||||||
|
[
|
||||||
|
'type' => 'section',
|
||||||
|
'text' => [
|
||||||
|
'type' => 'mrkdwn',
|
||||||
|
'text' => 'New submission for your form *<'.$formURL.'|'.$event->form->title.':>*',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'section',
|
||||||
|
'text' => [
|
||||||
|
'type' => 'mrkdwn',
|
||||||
|
'text' => $submissionString
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'section',
|
||||||
|
'text' => [
|
||||||
|
'type' => 'mrkdwn',
|
||||||
|
'text' => '*<'.$formURL.'|🔗 Open Form>* *<'.$editFormURL.'|✍️ Edit Form>*',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
WebhookCall::create()
|
||||||
|
->url($event->form->slack_webhook_url)
|
||||||
|
->doNotSign()
|
||||||
|
->payload($finalSlackPostData)
|
||||||
|
->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateSlackWebhookUrl($url)
|
||||||
|
{
|
||||||
|
return ($url) ? str_contains($url, 'https://hooks.slack.com/') : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Form extends Model
|
||||||
'notification_subject',
|
'notification_subject',
|
||||||
'notification_body',
|
'notification_body',
|
||||||
'notifications_include_submission',
|
'notifications_include_submission',
|
||||||
|
'slack_webhook_url',
|
||||||
|
|
||||||
// integrations
|
// integrations
|
||||||
'webhook_url',
|
'webhook_url',
|
||||||
|
@ -94,6 +95,7 @@ class Form extends Model
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'workspace_id',
|
'workspace_id',
|
||||||
'notifies',
|
'notifies',
|
||||||
|
'slack_webhook_url',
|
||||||
'webhook_url',
|
'webhook_url',
|
||||||
'send_submission_confirmation',
|
'send_submission_confirmation',
|
||||||
'redirect_url',
|
'redirect_url',
|
||||||
|
@ -223,4 +225,9 @@ class Form extends Model
|
||||||
{
|
{
|
||||||
return FormFactory::new();
|
return FormFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getNotifiesSlackAttribute()
|
||||||
|
{
|
||||||
|
return !empty($this->slack_webhook_url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ namespace App\Models;
|
||||||
|
|
||||||
use App\Http\Controllers\SubscriptionController;
|
use App\Http\Controllers\SubscriptionController;
|
||||||
use App\Models\Forms\Form;
|
use App\Models\Forms\Form;
|
||||||
use App\Models\Workspace;
|
|
||||||
use App\Notifications\ResetPassword;
|
use App\Notifications\ResetPassword;
|
||||||
use App\Notifications\VerifyEmail;
|
use App\Notifications\VerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
@ -13,7 +12,6 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Laravel\Cashier\Billable;
|
use Laravel\Cashier\Billable;
|
||||||
use Rickycezar\Impersonate\Models\Impersonate;
|
|
||||||
use Tymon\JWTAuth\Contracts\JWTSubject;
|
use Tymon\JWTAuth\Contracts\JWTSubject;
|
||||||
|
|
||||||
class User extends Authenticatable implements JWTSubject //, MustVerifyEmail
|
class User extends Authenticatable implements JWTSubject //, MustVerifyEmail
|
||||||
|
|
|
@ -38,6 +38,7 @@ class FormCleaner
|
||||||
'theme' => 'default',
|
'theme' => 'default',
|
||||||
'use_captcha' => false,
|
'use_captcha' => false,
|
||||||
'password' => null,
|
'password' => null,
|
||||||
|
'slack_webhook_url' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
private array $fieldDefaults = [
|
private array $fieldDefaults = [
|
||||||
|
@ -68,6 +69,7 @@ class FormCleaner
|
||||||
'logo_picture' => 'The logo was removed.',
|
'logo_picture' => 'The logo was removed.',
|
||||||
'database_fields_update' => 'Form submission will only create new records (no updates).',
|
'database_fields_update' => 'Form submission will only create new records (no updates).',
|
||||||
'theme' => 'Default theme was applied.',
|
'theme' => 'Default theme was applied.',
|
||||||
|
'slack_webhook_url' => "Slack webhook disabled.",
|
||||||
|
|
||||||
// For fields
|
// For fields
|
||||||
'hide_field_name' => 'Hide field name removed.',
|
'hide_field_name' => 'Hide field name removed.',
|
||||||
|
|
|
@ -46,5 +46,9 @@ return [
|
||||||
|
|
||||||
'notion' => [
|
'notion' => [
|
||||||
'worker' => env('NOTION_WORKER','https://notion-forms-worker.notionforms.workers.dev/v1')
|
'worker' => env('NOTION_WORKER','https://notion-forms-worker.notionforms.workers.dev/v1')
|
||||||
]
|
],
|
||||||
|
|
||||||
|
'google_analytics_code' => env('GOOGLE_ANALYTICS_CODE'),
|
||||||
|
'amplitude_code' => env('AMPLITUDE_CODE'),
|
||||||
|
'crisp_website_id' => env('CRISP_WEBSITE_ID')
|
||||||
];
|
];
|
||||||
|
|
|
@ -57,7 +57,7 @@ class FormFactory extends Factory
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'title' => $this->faker->text(30),
|
'title' => $this->faker->text(30),
|
||||||
'description' => $this->faker->randomHtml(2),
|
'description' => $this->faker->randomHtml(1),
|
||||||
'notifies' => false,
|
'notifies' => false,
|
||||||
'send_submission_confirmation' => false,
|
'send_submission_confirmation' => false,
|
||||||
'webhook_url' => null,
|
'webhook_url' => null,
|
||||||
|
@ -80,7 +80,8 @@ class FormFactory extends Factory
|
||||||
'use_captcha' => false,
|
'use_captcha' => false,
|
||||||
'can_be_indexed' => true,
|
'can_be_indexed' => true,
|
||||||
'password' => false,
|
'password' => false,
|
||||||
'tags' => []
|
'tags' => [],
|
||||||
|
'slack_webhook_url' => null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->string('slack_webhook_url')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('slack_webhook_url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -11,7 +11,8 @@
|
||||||
:style="inputStyle" :name="name" :fixed-classes="fixedClasses" :range="dateRange"
|
:style="inputStyle" :name="name" :fixed-classes="fixedClasses" :range="dateRange"
|
||||||
:placeholder="placeholder" :timepicker="useTime"
|
:placeholder="placeholder" :timepicker="useTime"
|
||||||
:date-format="useTime?'Z':'Y-m-d'"
|
:date-format="useTime?'Z':'Y-m-d'"
|
||||||
:user-format="useTime?'F j, Y - H:i':'F j, Y'"
|
:user-format="useTime ? amPm ? 'F j, Y - G:i K' : 'F j, Y - H:i' : 'F j, Y'"
|
||||||
|
:amPm="amPm"
|
||||||
/>
|
/>
|
||||||
<small v-if="help" :class="theme.default.help">
|
<small v-if="help" :class="theme.default.help">
|
||||||
<slot name="help">{{ help }}</slot>
|
<slot name="help">{{ help }}</slot>
|
||||||
|
@ -30,7 +31,8 @@ export default {
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
withTime: { type: Boolean, default: false },
|
withTime: { type: Boolean, default: false },
|
||||||
dateRange: { type: Boolean, default: false }
|
dateRange: { type: Boolean, default: false },
|
||||||
|
amPm: { type: Boolean, default: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
|
|
@ -1,86 +1,84 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<label v-if="label" :for="id?id:name"
|
<label v-if="label" :for="id?id:name"
|
||||||
:class="[theme.default.label,{'uppercase text-xs':uppercaseLabels, 'text-sm':!uppercaseLabels}]"
|
:class="[theme.default.label,{'uppercase text-xs':uppercaseLabels, 'text-sm':!uppercaseLabels}]"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
<span v-if="required" class="text-red-500 required-dot">*</span>
|
<span v-if="required" class="text-red-500 required-dot">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<loader v-if="loading" key="loader" class="h-6 w-6 text-nt-blue mx-auto" />
|
<loader v-if="loading" key="loader" class="h-6 w-6 text-nt-blue mx-auto" />
|
||||||
<div v-else v-for="option in options" :key="option[optionKey]" class="flex border mb-4 p-3 cursor-pointer rounded-2xl" @click="onSelect(option[optionKey])">
|
<div v-for="(option, index) in options" v-else :key="option[optionKey]"
|
||||||
<p class="flex-grow">
|
:class="[theme.default.input,'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-900 flex',{ 'mb-2': index !== options.length,'ring-red-500 ring-2': hasValidation && form.errors.has(name), 'cursor-not-allowed bg-gray-200':disabled }]"
|
||||||
{{ option[displayKey] }}
|
@click="onSelect(option[optionKey])"
|
||||||
</p>
|
>
|
||||||
<span v-if="isSelected(option[optionKey])" class="float-right">
|
<p class="flex-grow">
|
||||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
{{ option[displayKey] }}
|
||||||
<path fill-rule="evenodd"
|
</p>
|
||||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
<div v-if="isSelected(option[optionKey])" class="flex items-center">
|
||||||
clip-rule="evenodd"
|
<svg :color="color" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
/>
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<small v-if="help" :class="theme.SelectInput.help">
|
|
||||||
<slot name="help">{{ help }}</slot>
|
|
||||||
</small>
|
|
||||||
<has-error v-if="hasValidation" :form="form" :field="name" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<small v-if="help" :class="theme.SelectInput.help">
|
||||||
import inputMixin from '~/mixins/forms/input'
|
<slot name="help">{{ help }}</slot>
|
||||||
|
</small>
|
||||||
|
<has-error v-if="hasValidation" :form="form" :field="name" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
/**
|
<script>
|
||||||
* Options: {name,value} objects
|
import inputMixin from '~/mixins/forms/input'
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
name: 'FlatSelectInput',
|
|
||||||
mixins: [inputMixin],
|
|
||||||
|
|
||||||
props: {
|
/**
|
||||||
options: { type: Array, required: true },
|
* Options: {name,value} objects
|
||||||
optionKey: { type: String, default: 'value' },
|
*/
|
||||||
emitKey: { type: String, default: 'value' },
|
export default {
|
||||||
displayKey: { type: String, default: 'name' },
|
name: 'FlatSelectInput',
|
||||||
loading: { type: Boolean, default: false },
|
mixins: [inputMixin],
|
||||||
multiple: { type: Boolean, default: false },
|
|
||||||
},
|
props: {
|
||||||
data () {
|
options: { type: Array, required: true },
|
||||||
return {
|
optionKey: { type: String, default: 'value' },
|
||||||
|
emitKey: { type: String, default: 'value' },
|
||||||
|
displayKey: { type: String, default: 'name' },
|
||||||
|
loading: { type: Boolean, default: false },
|
||||||
|
multiple: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
methods: {
|
||||||
|
onSelect (value) {
|
||||||
|
if (this.multiple) {
|
||||||
|
const emitValue = Array.isArray(this.compVal) ? [...this.compVal] : []
|
||||||
|
|
||||||
|
// Already in value, remove it
|
||||||
|
if (this.isSelected(value)) {
|
||||||
|
this.compVal = emitValue.filter((item) => {
|
||||||
|
return item !== value
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise add value
|
||||||
|
emitValue.push(value)
|
||||||
|
this.compVal = emitValue
|
||||||
|
} else {
|
||||||
|
this.compVal = (this.compVal === value) ? null : value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
isSelected (value) {
|
||||||
},
|
if (!this.compVal) return false
|
||||||
methods: {
|
|
||||||
onSelect (value) {
|
|
||||||
if (this.multiple) {
|
|
||||||
const emitValue = Array.isArray(this.compVal) ? [...this.compVal] : []
|
|
||||||
|
|
||||||
// Already in value, remove it
|
if (this.multiple) {
|
||||||
if (this.isSelected(value)) {
|
return this.compVal.includes(value)
|
||||||
this.compVal = emitValue.filter((item) => {
|
|
||||||
return item !== value
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise add value
|
|
||||||
emitValue.push(value)
|
|
||||||
this.compVal = emitValue
|
|
||||||
} else {
|
|
||||||
this.compVal = (this.compVal === value) ? null : value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isSelected (value) {
|
|
||||||
if(!this.compVal) return false
|
|
||||||
|
|
||||||
if (this.multiple) {
|
|
||||||
return this.compVal.includes(value)
|
|
||||||
}
|
|
||||||
return this.compVal === value
|
|
||||||
}
|
}
|
||||||
|
return this.compVal === value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -288,6 +288,12 @@ export default {
|
||||||
} else if (urlPrefill && urlPrefill.has(field.id + '[]')) {
|
} else if (urlPrefill && urlPrefill.has(field.id + '[]')) {
|
||||||
// Array url prefills
|
// Array url prefills
|
||||||
formData[field.id] = urlPrefill.getAll(field.id + '[]')
|
formData[field.id] = urlPrefill.getAll(field.id + '[]')
|
||||||
|
} else if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
|
||||||
|
const dateObj = new Date()
|
||||||
|
const currentDate = dateObj.getFullYear() + '-' +
|
||||||
|
String(dateObj.getMonth() + 1).padStart(2, '0') + '-' +
|
||||||
|
String(dateObj.getDate()).padStart(2, '0')
|
||||||
|
formData[field.id] = currentDate
|
||||||
} else { // Default prefill if any
|
} else { // Default prefill if any
|
||||||
formData[field.id] = field.prefill
|
formData[field.id] = field.prefill
|
||||||
}
|
}
|
||||||
|
@ -363,6 +369,9 @@ export default {
|
||||||
} else if (field.date_range) {
|
} else if (field.date_range) {
|
||||||
inputProperties.dateRange = true
|
inputProperties.dateRange = true
|
||||||
}
|
}
|
||||||
|
if (field.use_am_pm) {
|
||||||
|
inputProperties.amPm = true
|
||||||
|
}
|
||||||
} else if (field.type === 'files' || (field.type === 'url' && field.file_upload)) {
|
} else if (field.type === 'files' || (field.type === 'url' && field.file_upload)) {
|
||||||
inputProperties.multiple = (field.multiple !== undefined && field.multiple)
|
inputProperties.multiple = (field.multiple !== undefined && field.multiple)
|
||||||
inputProperties.mbLimit = 5
|
inputProperties.mbLimit = 5
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
<collapse class="p-5 w-full border-b">
|
<collapse class="p-5 w-full border-b">
|
||||||
<template #title>
|
<template #title>
|
||||||
<h3 class="font-semibold text-lg">
|
<h3 class="font-semibold text-lg">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline text-gray-500 mr-2 -mt-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline text-gray-500 mr-2 -mt-1" fill="none"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z" />
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z"/>
|
||||||
</svg>
|
</svg>
|
||||||
Integrations
|
Integrations
|
||||||
<pro-tag />
|
<pro-tag/>
|
||||||
</h3>
|
</h3>
|
||||||
</template>
|
</template>
|
||||||
<text-input name="webhook_url" class="mt-4"
|
<text-input name="webhook_url" class="mt-4"
|
||||||
|
@ -14,24 +16,26 @@
|
||||||
label="Webhook URL"
|
label="Webhook URL"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p>
|
<!-- <div>-->
|
||||||
<span class="text-uppercase font-semibold text-blue-500">NEW</span> - our Zapier integration is available for
|
<!-- <p>-->
|
||||||
beta testers! During the beta, <b>you don't need a Pro subscription</b> to try it out.
|
<!-- <span class="text-uppercase font-semibold text-blue-500">NEW</span> - our Zapier integration is available for-->
|
||||||
</p>
|
<!-- beta testers!-->
|
||||||
<p class="w-full text-center mt-5">
|
<!-- </p>-->
|
||||||
<a :href="zapierUrl" target="_blank">
|
<!-- <p class="w-full text-center mt-5">-->
|
||||||
<v-button color="gray" shade="lighter">
|
<!-- <a :href="zapierUrl" target="_blank">-->
|
||||||
<svg class="h-5 w-5 inline text-yellow-500" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
|
<!-- <v-button color="gray" shade="lighter">-->
|
||||||
viewBox="0 0 512 512"
|
<!-- <svg class="h-5 w-5 inline text-yellow-500" fill="currentColor" xmlns="http://www.w3.org/2000/svg"-->
|
||||||
>
|
<!-- viewBox="0 0 512 512"-->
|
||||||
<path
|
<!-- >-->
|
||||||
d="M318 256c0 19-4 36-10 52-16 7-34 10-52 10-19 0-36-3-52-9-7-17-10-34-10-53 0-18 3-36 10-52 16-6 33-10 52-10 18 0 36 4 52 10 6 16 10 34 10 52zm182-41H355l102-102c-8-11-17-22-26-32-10-9-21-18-32-26L297 157V12c-13-2-27-3-41-3s-28 1-41 3v145L113 55c-12 8-22 17-32 26-10 10-19 21-27 32l102 102H12s-3 27-3 41 1 28 3 41h144L54 399c16 23 36 43 59 59l102-102v144c13 2 27 3 41 3s28-1 41-3V356l102 102c11-8 22-17 32-27 9-10 18-20 26-32L355 297h145c2-13 3-27 3-41s-1-28-3-41z"
|
<!-- <path-->
|
||||||
/>
|
<!-- d="M318 256c0 19-4 36-10 52-16 7-34 10-52 10-19 0-36-3-52-9-7-17-10-34-10-53 0-18 3-36 10-52 16-6 33-10 52-10 18 0 36 4 52 10 6 16 10 34 10 52zm182-41H355l102-102c-8-11-17-22-26-32-10-9-21-18-32-26L297 157V12c-13-2-27-3-41-3s-28 1-41 3v145L113 55c-12 8-22 17-32 26-10 10-19 21-27 32l102 102H12s-3 27-3 41 1 28 3 41h144L54 399c16 23 36 43 59 59l102-102v144c13 2 27 3 41 3s28-1 41-3V356l102 102c11-8 22-17 32-27 9-10 18-20 26-32L355 297h145c2-13 3-27 3-41s-1-28-3-41z"-->
|
||||||
</svg>
|
<!-- />-->
|
||||||
Zapier Integration
|
<!-- </svg>-->
|
||||||
</v-button>
|
<!-- Zapier Integration-->
|
||||||
</a>
|
<!-- </v-button>-->
|
||||||
</p>
|
<!-- </a>-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- </div>-->
|
||||||
</collapse>
|
</collapse>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -40,21 +44,19 @@ import Collapse from '../../../../common/Collapse'
|
||||||
import ProTag from '../../../../common/ProTag'
|
import ProTag from '../../../../common/ProTag'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Collapse, ProTag },
|
components: {Collapse, ProTag},
|
||||||
props: {
|
props: {},
|
||||||
},
|
data() {
|
||||||
data () {
|
return {}
|
||||||
return {
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
form: {
|
form: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state['open/working_form'].content
|
return this.$store.state['open/working_form'].content
|
||||||
},
|
},
|
||||||
/* We add a setter */
|
/* We add a setter */
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit('open/working_form/set', value)
|
this.$store.commit('open/working_form/set', value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -64,10 +66,9 @@ export default {
|
||||||
|
|
||||||
watch: {},
|
watch: {},
|
||||||
|
|
||||||
mounted () {
|
mounted() {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,6 +15,17 @@
|
||||||
<text-area-input v-if="form.notifies" name="notification_emails" :form="form" class="mt-4"
|
<text-area-input v-if="form.notifies" name="notification_emails" :form="form" class="mt-4"
|
||||||
label="Notification Emails" help="Add one email per line"
|
label="Notification Emails" help="Add one email per line"
|
||||||
/>
|
/>
|
||||||
|
<checkbox-input name="notifies_slack" :form="form" class="mt-4"
|
||||||
|
label="Receive a Slack notification on submission"
|
||||||
|
/>
|
||||||
|
<text-input v-if="form.notifies_slack" name="slack_webhook_url" :form="form" class="mt-4"
|
||||||
|
label="Slack webhook url" help="help"
|
||||||
|
>
|
||||||
|
<template #help>
|
||||||
|
Receive slack message on each form submission. <a href="https://api.slack.com/messaging/webhooks" target="_blank">Click here</a> to learn how to get a slack webhook url
|
||||||
|
</template>
|
||||||
|
</text-input>
|
||||||
|
|
||||||
<checkbox-input :disabled="emailSubmissionConfirmationField===null" name="send_submission_confirmation"
|
<checkbox-input :disabled="emailSubmissionConfirmationField===null" name="send_submission_confirmation"
|
||||||
:form="form" class="mt-4"
|
:form="form" class="mt-4"
|
||||||
label="Send submission confirmation" :help="emailSubmissionConfirmationHelp"
|
label="Send submission confirmation" :help="emailSubmissionConfirmationHelp"
|
||||||
|
|
|
@ -111,12 +111,30 @@
|
||||||
<p class="text-gray-400 mb-5">
|
<p class="text-gray-400 mb-5">
|
||||||
Include time. Or not. This cannot be used with the date range option yet.
|
Include time. Or not. This cannot be used with the date range option yet.
|
||||||
</p>
|
</p>
|
||||||
|
<v-checkbox v-if="field.with_time"
|
||||||
|
v-model="field.use_am_pm"
|
||||||
|
name="use_am_pm"
|
||||||
|
>
|
||||||
|
Use 12h AM/PM format
|
||||||
|
</v-checkbox>
|
||||||
|
<p v-if="field.with_time" class="text-gray-400 mb-5">
|
||||||
|
By default, input uses the 24 hours format
|
||||||
|
</p>
|
||||||
|
|
||||||
<select-input v-if="field.with_time" name="timezone" class="mt-4"
|
<select-input v-if="field.with_time" name="timezone" class="mt-4"
|
||||||
:form="field" :options="timezonesOptions"
|
:form="field" :options="timezonesOptions"
|
||||||
label="Timezone" :searchable="true"
|
label="Timezone" :searchable="true"
|
||||||
help="Make sure to select correct timezone. Leave blank otherwise."
|
help="Make sure to select correct timezone. Leave blank otherwise."
|
||||||
/>
|
/>
|
||||||
|
<v-checkbox v-model="field.prefill_today"
|
||||||
|
name="prefill_today"
|
||||||
|
@input="onFieldPrefillTodayChange"
|
||||||
|
>
|
||||||
|
Prefill with 'today'
|
||||||
|
</v-checkbox>
|
||||||
|
<p class="text-gray-400 mb-5">
|
||||||
|
if enabled we will pre-fill this field with the current date
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- select/multiselect Options -->
|
<!-- select/multiselect Options -->
|
||||||
|
@ -180,6 +198,11 @@
|
||||||
label="Pre-filled value"
|
label="Pre-filled value"
|
||||||
:multiple="field.type==='multi_select'"
|
:multiple="field.type==='multi_select'"
|
||||||
/>
|
/>
|
||||||
|
<date-input v-else-if="field.type==='date' && field.prefill_today!==true" name="prefill" class="mt-4"
|
||||||
|
:form="field" :with-time="field.with_time===true" :am-pm="field.use_am_pm===true"
|
||||||
|
:date-range="field.date_range===true"
|
||||||
|
label="Pre-filled value"
|
||||||
|
/>
|
||||||
<text-area-input v-else-if="field.type === 'text' && field.multi_lines"
|
<text-area-input v-else-if="field.type === 'text' && field.multi_lines"
|
||||||
name="prefill" class="mt-4"
|
name="prefill" class="mt-4"
|
||||||
:form="field"
|
:form="field"
|
||||||
|
@ -188,6 +211,7 @@
|
||||||
<text-input v-else-if="field.type!=='files'" name="prefill" class="mt-4"
|
<text-input v-else-if="field.type!=='files'" name="prefill" class="mt-4"
|
||||||
:form="field"
|
:form="field"
|
||||||
label="Pre-filled value"
|
label="Pre-filled value"
|
||||||
|
:disabled="field.type==='date' && field.prefill_today===true"
|
||||||
/>
|
/>
|
||||||
<div v-if="['select','multi_select'].includes(field.type)" class="-mt-3 mb-3 text-gray-400 dark:text-gray-500">
|
<div v-if="['select','multi_select'].includes(field.type)" class="-mt-3 mb-3 text-gray-400 dark:text-gray-500">
|
||||||
<small>
|
<small>
|
||||||
|
@ -383,10 +407,12 @@ export default {
|
||||||
this.$set(this.field, 'date_range', val)
|
this.$set(this.field, 'date_range', val)
|
||||||
if (this.field.date_range) {
|
if (this.field.date_range) {
|
||||||
this.$set(this.field, 'with_time', false)
|
this.$set(this.field, 'with_time', false)
|
||||||
|
this.$set(this.field, 'prefill_today', false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFieldWithTimeChange (val) {
|
onFieldWithTimeChange (val) {
|
||||||
this.$set(this.field, 'with_time', val)
|
this.$set(this.field, 'with_time', val)
|
||||||
|
this.$set(this.field, 'use_am_pm', false)
|
||||||
if (this.field.with_time) {
|
if (this.field.with_time) {
|
||||||
this.$set(this.field, 'date_range', false)
|
this.$set(this.field, 'date_range', false)
|
||||||
}
|
}
|
||||||
|
@ -420,6 +446,15 @@ export default {
|
||||||
})
|
})
|
||||||
this.$set(this.field, this.field.type, {'options': tmpOpts})
|
this.$set(this.field, this.field.type, {'options': tmpOpts})
|
||||||
},
|
},
|
||||||
|
onFieldPrefillTodayChange (val) {
|
||||||
|
this.$set(this.field, 'prefill_today', val)
|
||||||
|
if (this.field.prefill_today) {
|
||||||
|
this.$set(this.field, 'prefill', 'Pre-filled with current date')
|
||||||
|
this.$set(this.field, 'date_range', false)
|
||||||
|
} else {
|
||||||
|
this.$set(this.field, 'prefill', null)
|
||||||
|
}
|
||||||
|
},
|
||||||
onFieldAllowCreationChange (val) {
|
onFieldAllowCreationChange (val) {
|
||||||
this.$set(this.field, 'allow_creation', val)
|
this.$set(this.field, 'allow_creation', val)
|
||||||
if(this.field.allow_creation){
|
if(this.field.allow_creation){
|
||||||
|
|
|
@ -44,7 +44,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loadAmplitude () {
|
loadAmplitude () {
|
||||||
if (this.loaded || !typeof window.amplitude === 'undefined') return
|
if (this.loaded || !typeof window.amplitude === 'undefined' || !window.config.amplitude_code) return
|
||||||
|
|
||||||
(function (e, t) {
|
(function (e, t) {
|
||||||
const n = e.amplitude || { _q: [], _iq: {} }; const r = t.createElement('script')
|
const n = e.amplitude || { _q: [], _iq: {} }; const r = t.createElement('script')
|
||||||
|
@ -88,7 +88,7 @@ export default {
|
||||||
})(window, document)
|
})(window, document)
|
||||||
|
|
||||||
this.amplitudeInstance = window.amplitude.getInstance()
|
this.amplitudeInstance = window.amplitude.getInstance()
|
||||||
this.amplitudeInstance.init('9952c8b914ce3f2bd494fce2dba18243')
|
this.amplitudeInstance.init(window.config.amplitude_code)
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
this.authenticateUser()
|
this.authenticateUser()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadCrisp () {
|
loadCrisp () {
|
||||||
if (this.isIframe) return
|
if (this.isIframe || !window.config.crisp_website_id) return
|
||||||
|
|
||||||
window.$crisp = []
|
window.$crisp = []
|
||||||
window.CRISP_WEBSITE_ID = '94219d77-06ff-4aec-b07a-5bf26ec8fde1'
|
window.CRISP_WEBSITE_ID = window.config.crisp_website_id
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
script.setAttribute('src', 'https://client.crisp.chat/l.js')
|
script.setAttribute('src', 'https://client.crisp.chat/l.js')
|
||||||
|
|
|
@ -152,6 +152,7 @@ export default {
|
||||||
properties: [],
|
properties: [],
|
||||||
|
|
||||||
notifies: false,
|
notifies: false,
|
||||||
|
slack_notifies: false,
|
||||||
send_submission_confirmation: false,
|
send_submission_confirmation: false,
|
||||||
webhook_url: null,
|
webhook_url: null,
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
'links' => config('links'),
|
'links' => config('links'),
|
||||||
'production' => App::isProduction(),
|
'production' => App::isProduction(),
|
||||||
'hCaptchaSiteKey' => config('services.h_captcha.site_key'),
|
'hCaptchaSiteKey' => config('services.h_captcha.site_key'),
|
||||||
|
'google_analytics_code' => config('services.google_analytics_code'),
|
||||||
|
'amplitude_code' => config('services.amplitude_code'),
|
||||||
|
'crisp_website_id' => config('services.crisp_website_id'),
|
||||||
];
|
];
|
||||||
@endphp
|
@endphp
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
@ -37,8 +40,9 @@
|
||||||
{{-- Load the application scripts --}}
|
{{-- Load the application scripts --}}
|
||||||
<script src="{{ mix('dist/js/app.js') }}"></script>
|
<script src="{{ mix('dist/js/app.js') }}"></script>
|
||||||
|
|
||||||
|
@if($config['google_analytics_code'])
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-PR916W6KJZ"></script>
|
<script defer src="https://www.googletagmanager.com/gtag/js?id={{ $config['google_analytics_code'] }}"></script>
|
||||||
<script defer>
|
<script defer>
|
||||||
window.dataLayer = window.dataLayer || []
|
window.dataLayer = window.dataLayer || []
|
||||||
|
|
||||||
|
@ -46,7 +50,9 @@
|
||||||
|
|
||||||
gtag('js', new Date())
|
gtag('js', new Date())
|
||||||
|
|
||||||
gtag('config', 'G-PR916W6KJZ')
|
gtag('config', "{{ $config['google_analytics_code'] }}")
|
||||||
</script>
|
</script>
|
||||||
|
@endif
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue