diff --git a/.babelrc b/.babelrc
new file mode 100644
index 000000000..c4701e7d0
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,8 @@
+{
+ "presets": [
+ "@babel/preset-env"
+ ],
+ "plugins": [
+ "@babel/plugin-syntax-dynamic-import"
+ ]
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..0c2e7fe45
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{vue,js,json,html,scss,blade.php,yml}]
+indent_size = 2
diff --git a/.env.example b/.env.example
new file mode 100644
index 000000000..18a63bd9f
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,57 @@
+APP_NAME="Laravel Vue Tailwind Spa"
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_LOG_LEVEL=debug
+APP_URL=http://localhost
+
+LOG_CHANNEL=stack
+LOG_LEVEL=debug
+
+DB_CONNECTION=pgsql
+DB_HOST=127.0.0.1
+DB_PORT=5432
+DB_DATABASE=postgres
+DB_USERNAME=postgres
+DB_PASSWORD=postgres
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+QUEUE_CONNECTION=sync
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=smtp
+MAIL_HOST=smtp.mailtrap.io
+MAIL_PORT=2525
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_FROM_ADDRESS=null
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+
+PUSHER_APP_ID=
+PUSHER_APP_KEY=
+PUSHER_APP_SECRET=
+PUSHER_APP_CLUSTER=mt1
+
+MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
+JWT_TTL=1440
+JWT_SECRET=
+
+STRIPE_KEY=
+STRIPE_SECRET=
+
+MUX_WORKSPACE_ID=
+MUX_API_TOKEN=
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..156209320
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,16 @@
+module.exports = {
+ root: true,
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ parser: '@babel/eslint-parser',
+ ecmaVersion: 2018,
+ sourceType: 'module'
+ },
+ extends: [
+ 'plugin:vue/recommended',
+ 'standard'
+ ],
+ rules: {
+ 'vue/max-attributes-per-line': 'off'
+ }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..81f8ed652
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+* text=auto
+*.css linguist-vendored
+*.scss linguist-vendored
+*.js linguist-vendored
+CHANGELOG.md export-ignore
+README.md export-ignore
+.travis.yml export-ignore
+.env.dusk.local export-ignore
+.env.dusk.testing export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..f6ccd1fd4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+/node_modules
+/public/hot
+/public/storage
+/storage/*.key
+/vendor
+.env
+.env.production
+.env.backup
+.env.testing
+.env.dusk.local
+.phpunit.result.cache
+.idea/*
+Homestead.json
+Homestead.yaml
+npm-debug.log
+yarn-error.log
+phpunit.dusk.xml
+/public/dist
+/public/build
+/public/mix-manifest.json
+/public/sitemap.xml
+.DS_Store
+public/.DS_Store
+*.DS_Store
+.vapor/
+.env.production
+.env.staging
diff --git a/.styleci.yml b/.styleci.yml
new file mode 100644
index 000000000..9231873a1
--- /dev/null
+++ b/.styleci.yml
@@ -0,0 +1,13 @@
+php:
+ preset: laravel
+ disabled:
+ - no_unused_imports
+ finder:
+ not-name:
+ - index.php
+ - server.php
+js:
+ finder:
+ not-name:
+ - webpack.mix.js
+css: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..eba890f93
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,105 @@
+# Changelog
+
+## 5.0.0 - 2020-10-31
+
+- Upgrade to Laravel 8
+- Update dependencies
+- Fixed locale cookie fallback [#221](https://github.com/cretueusebiu/laravel-vue-spa/pull/221)
+- Added scroll delay [#220](https://github.com/cretueusebiu/laravel-vue-spa/pull/220)
+- Added redirect to intended url [#208](https://github.com/cretueusebiu/laravel-vue-spa/pull/208)
+- Added middleware parameter [#124](https://github.com/cretueusebiu/laravel-vue-spa/pull/124)
+
+## 4.7.0 - 2020-04-09
+
+- Added `routes/spa.php` and `spa` middleware for serving the frontend [#249](https://github.com/cretueusebiu/laravel-vue-spa/pull/249)
+- Added controllers for user and spa view to fix route caching [#248](https://github.com/cretueusebiu/laravel-vue-spa/pull/248)
+
+## 4.6.0 - 2020-03-25
+
+- Upgrade to Laravel 7
+
+## 4.5.0 - 2019-09-29
+
+- Upgrade to Laravel 6
+
+## 4.4.0 - 2019-05-17
+
+- Upgrade to Laravel 5.8
+- Update npm dependencies
+- Added email verification
+- Removed yarn.lock in favor of package-lock.json
+- Fixed `laravel-mix` (HMR, versioning, build cleanup), thanks to [TemaSM](https://github.com/TemaSM)
+- Removed JS polyfills, if you want to support IE11 see [laravel-mix#436](https://github.com/JeffreyWay/laravel-mix/issues/436)
+
+## 4.3.0 - 2018-10-21
+
+- Fixed npm install
+- Update fontawesome
+
+## 4.2.0 - 2018-10-04
+
+- Upgrade to Laravel 5.7
+
+## 4.1.1 - 2018-05-10
+
+- Fixed oauth popup
+- Fixed session expired alert
+- Update npm dependencies
+
+## 4.1.0 - 2018-03-14
+
+- Upgrade to Laravel 5.6
+- Update npm dependencies
+- Enabled Mix [esModule](https://github.com/JeffreyWay/laravel-mix/pull/1526#issuecomment-373044182) by default
+- Lint with eslint-plugin-vue and eslint-config-standard
+
+## 4.0.0 - 2018-01-23
+
+- Brought back the `layout` property in pages that was removed in 3.0. By default it's set to `default` (`~/layouts/default.vue`).
+
+## 3.0.1 - 2018-01-22
+
+- Removed middleware from routes, since they don't work when you press the back button. Now you have to use the `middleware` property in pages.
+
+## 3.0.0 - 2018-01-22
+
+- Removed `layout` property from pages in favor of explicit layout components
+- Improved middleware system, you can either define it on a route or directly on a component
+- Reorganized components
+- Change jwt token ttl to one day
+- Fixed hot module replacement
+
+## 2.2.1 - 2018-01-19
+
+- Upgrade to Bootstrap 4
+
+## 2.2.0 - 2018-01-16
+
+- Added dynamic import for pages
+- Added locale with dynamic import
+- Import shakable fontawesome module
+
+## 2.1.0 - 2018-01-10
+
+- Simplifyd password reset
+- Upgrade to Bootstrap 4 beta3
+- Added oauth with popup window
+
+## 2.0.0 - 2017-12-19
+
+- Added support for Socialite
+- Added dropdown to select the locale
+- Added route middleware
+- Added Font Awesome 5 icons
+- Added user profile photo in navbar
+- Added Chinese and Spanish translations
+- Upgrade to Bootstrap 4 beta2
+- Fixed router scroll behaviour
+- Namespaced store modules
+- Published project to Packagist
+
+## 1.0.0 - 2017-09-02
+
+- Upgrade to Laravel 5.5.
+- Upgrade to Bootstrap 4 beta1.
+- Rework routing and guards.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..8143e0ab9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Cretu Eusebiu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..44ec54d98
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+# Laravel-Vue-Tailwind SPA
+
+
+
+
+
+> A Laravel-Vue-Tailwind SPA starter kit. Forked from [cretueusebiu/laravel-vue-spa](https://github.com/cretueusebiu/laravel-vue-spa).
+
+
+
+
Test
', + | + | Will be cleaned to: 'Test' + | + | http://htmlpurifier.org/live/configdoc/plain.html#HTML.ForbiddenElements + | + */ + + 'HTML.ForbiddenElements' => '', + + /* + |-------------------------------------------------------------------------- + | CSS.AllowedProperties + |-------------------------------------------------------------------------- + | + | The Allowed CSS properties. + | + | http://htmlpurifier.org/live/configdoc/plain.html#CSS.AllowedProperties + | + */ + + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + + /* + |-------------------------------------------------------------------------- + | AutoFormat.AutoParagraph + |-------------------------------------------------------------------------- + | + | The Allowed CSS properties. + | + | This directive turns on auto-paragraphing, where double + | newlines are converted in to paragraphs whenever possible. + | + | http://htmlpurifier.org/live/configdoc/plain.html#AutoFormat.AutoParagraph + | + */ + + 'AutoFormat.AutoParagraph' => false, + + /* + |-------------------------------------------------------------------------- + | AutoFormat.RemoveEmpty + |-------------------------------------------------------------------------- + | + | When enabled, HTML Purifier will attempt to remove empty + | elements that contribute no semantic information to the document. + | + | http://htmlpurifier.org/live/configdoc/plain.html#AutoFormat.RemoveEmpty + | + */ + + 'AutoFormat.RemoveEmpty' => false, + + /* + * Protect Links + */ + 'HTML.Nofollow' => true, + 'HTML.TargetBlank' => true, + 'HTML.TargetNoreferrer' => true, + 'HTML.TargetNoopener' => true, + + /* + * Allow notion link: allow Notion custom protocol + */ + 'URI.AllowedSchemes' => [ + 'http' => true, + 'https' => true, + 'mailto' => true, + 'tel' => true, + 'notion' => true, + ] + + ], + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 000000000..122229666 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,89 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => 0, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'your-queue-name'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/sentry.php b/config/sentry.php new file mode 100644 index 000000000..ea3e1a61c --- /dev/null +++ b/config/sentry.php @@ -0,0 +1,57 @@ + env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')), + + // capture release as git sha + // 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')), + + // When left empty or `null` the Laravel environment will be used + 'environment' => env('SENTRY_ENVIRONMENT'), + + 'breadcrumbs' => [ + // Capture Laravel logs in breadcrumbs + 'logs' => true, + + // Capture SQL queries in breadcrumbs + 'sql_queries' => true, + + // Capture bindings on SQL queries logged in breadcrumbs + 'sql_bindings' => true, + + // Capture queue job information in breadcrumbs + 'queue_info' => true, + + // Capture command information in breadcrumbs + 'command_info' => true, + ], + + 'tracing' => [ + // Trace queue jobs as their own transactions + 'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false), + + // Capture queue jobs as spans when executed on the sync driver + 'queue_jobs' => true, + + // Capture SQL queries as spans + 'sql_queries' => true, + + // Try to find out where the SQL query originated from and add it to the query spans + 'sql_origin' => true, + + // Capture views as spans + 'views' => true, + + // Indicates if the tracing integrations supplied by Sentry should be loaded + 'default_integrations' => true, + ], + + // @see: https://docs.sentry.io/platforms/php/configuration/options/#send-default-pii + 'send_default_pii' => false, + + 'traces_sample_rate' => (float)(env('SENTRY_TRACES_SAMPLE_RATE', 0.01)), + + 'controllers_base_namespace' => env('SENTRY_CONTROLLERS_BASE_NAMESPACE', 'App\\Http\\Controllers'), + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 000000000..e668b03cf --- /dev/null +++ b/config/services.php @@ -0,0 +1,50 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'eu-west-2'), + ], + + 'github' => [ + 'client_id' => env('GITHUB_CLIENT_ID'), + 'client_secret' => env('GITHUB_CLIENT_SECRET'), + ], + + 'h_captcha' => [ + 'site_key' => env('H_CAPTCHA_SITE_KEY'), + 'secret_key' => env('H_CAPTCHA_SECRET_KEY'), + ], + + 'canny' => [ + 'api_key' => env('CANNY_API_KEY'), + ], + + 'notion' => [ + 'worker' => env('NOTION_WORKER','https://notion-forms-worker.notionforms.workers.dev/v1') + ] +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 000000000..4e0f66cda --- /dev/null +++ b/config/session.php @@ -0,0 +1,201 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION', null), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | While using one of the framework's cache driven session backends you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE', null), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => env('SESSION_DOMAIN', null), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" since this is a secure default value. + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => 'lax', + +]; diff --git a/config/vapor-ui.php b/config/vapor-ui.php new file mode 100644 index 000000000..ec294735c --- /dev/null +++ b/config/vapor-ui.php @@ -0,0 +1,68 @@ + [ + 'api', + EnsureUserIsAuthorized::class, + EnsureEnvironmentVariables::class, + EnsureUpToDateAssets::class, + ], + + /* + |-------------------------------------------------------------------------- + | Vapor UI Domain + |-------------------------------------------------------------------------- + | + | This is the subdomain where Vapor UI will be accessible from. If this + | setting is null, Vapor UI will reside under the same domain as the + | application. Otherwise this value should serve as the subdomain. + | + */ + + 'domain' => env('VAPOR_UI_DOMAIN', null), + + /* + |-------------------------------------------------------------------------- + | Vapor UI Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Vapor UI will be accessible from. Feel free + | to change the path to anything you like. Note that the URI will not + | affect the paths of its internal API that isn't exposed to users. + | + */ + + 'path' => env('VAPOR_UI_PATH', 'vapor-ui'), + + /* + |-------------------------------------------------------------------------- + | Vapor UI Queues + |-------------------------------------------------------------------------- + | + | Typically, queues that should be monitored will be determined for you + | by Vapor UI. However, you are free to add additional queues to the + | list below in order to monitor queues that Vapor doesn't manage. + | + */ + + 'queues' => [ + + ], + +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 000000000..22b8a18d3 --- /dev/null +++ b/config/view.php @@ -0,0 +1,36 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), + +]; diff --git a/config/webhook-server.php b/config/webhook-server.php new file mode 100644 index 000000000..cb25c8342 --- /dev/null +++ b/config/webhook-server.php @@ -0,0 +1,71 @@ + env('SQS_QUEUE', 'your-queue-name'), + + /* + * The default queue connection that should be used to send webhook requests. + */ + 'connection' => null, + + /* + * The default http verb to use. + */ + 'http_verb' => 'post', + + /* + * This class is responsible for calculating the signature that will be added to + * the headers of the webhook request. A webhook client can use the signature + * to verify the request hasn't been tampered with. + */ + 'signer' => \Spatie\WebhookServer\Signer\DefaultSigner::class, + + /* + * This is the name of the header where the signature will be added. + */ + 'signature_header_name' => 'Signature', + + /* + * These are the headers that will be added to all webhook requests. + */ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + + /* + * If a call to a webhook takes longer that this amount of seconds + * the attempt will be considered failed. + */ + 'timeout_in_seconds' => 3, + + /* + * The amount of times the webhook should be called before we give up. + */ + 'tries' => 3, + + /* + * This class determines how many seconds there should be between attempts. + */ + 'backoff_strategy' => \Spatie\WebhookServer\BackoffStrategy\ExponentialBackoffStrategy::class, + + /* + * By default we will verify that the ssl certificate of the destination + * of the webhook is valid. + */ + 'verify_ssl' => true, + + /* + * When set to true, an exception will be thrown when the last attempt fails + */ + 'throw_exception_on_failure' => false, + + /* + * When using Laravel Horizon you can specify tags that should be used on the + * underlying job that performs the webhook request. + */ + 'tags' => [], +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 000000000..97fc97677 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1,2 @@ +*.sqlite +*.sqlite-journal diff --git a/database/factories/FormFactory.php b/database/factories/FormFactory.php new file mode 100644 index 000000000..7320977d1 --- /dev/null +++ b/database/factories/FormFactory.php @@ -0,0 +1,107 @@ +state(function (array $attributes) use ($workspace) { + return [ + 'workspace_id' => $workspace->id, + ]; + }); + } + + public function forDatabase(string $databaseId) + { + return $this->state(function (array $attributes) use ($databaseId) { + return [ + 'database_id' => $databaseId, + ]; + }); + } + + public function withProperties(array $properties) + { + return $this->state(function (array $attributes) use ($properties) { + return [ + 'properties' => $properties, + ]; + }); + } + + public function createdBy(User $user) + { + return $this->state(function (array $attributes) use ($user) { + return [ + 'creator_id' => $user->id, + ]; + }); + } + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'title' => $this->faker->text(30), + 'description' => $this->faker->randomHtml(2), + 'notifies' => false, + 'send_submission_confirmation' => false, + 'webhook_url' => null, + 'theme' => $this->faker->randomElement(Form::THEMES), + 'width' => $this->faker->randomElement(Form::WIDTHS), + 'dark_mode' => $this->faker->randomElement(Form::DARK_MODE_VALUES), + 'color' => '#3B82F6', + 'hide_title' => false, + 'no_branding' => false, + 'uppercase_labels' => true, + 'transparent_background' => false, + 'submit_button_text' => 'Submit', + 're_fillable' => false, + 're_fill_button_text' => 'Fill Again', + 'submitted_text' => 'Amazing, we saved your answers. Thank you for your time and have a great day!
', + 'notification_sender' => 'OpenForm', + 'notification_subject' => 'We saved your answers', + 'notification_body' => 'Hello there 👋Hello there 👋
This is a confirmation that your submission was successfully saved.
r&&(r=i),r},oe.numberOfLabelLines=function(e){var t=1;return oe.each(e,(function(e){oe.isArray(e)&&e.length>t&&(t=e.length)})),t},oe.color=q?function(e){return e instanceof CanvasGradient&&(e=Q.global.defaultColor),q(e)}:function(e){return e},oe.getHoverColor=function(e){return e instanceof CanvasPattern||e instanceof CanvasGradient?e:oe.color(e).saturate(.5).darken(.1).rgbString()}};function Vn(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function Jn(e){this.options=e||{}}oe.extend(Jn.prototype,{formats:Vn,parse:Vn,format:Vn,add:Vn,diff:Vn,startOf:Vn,endOf:Vn,_create:function(e){return e}}),Jn.override=function(e){oe.extend(Jn.prototype,e)};var Gn={_date:Jn},Kn={formatters:{values:function(e){return oe.isArray(e)?e:""+e},linear:function(e,t,n){var r=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(r)>1&&e!==Math.floor(e)&&(r=e-Math.floor(e));var a=oe.log10(Math.abs(r)),i="";if(0!==e)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=oe.log10(Math.abs(e)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),i=e.toExponential(s)}else{var c=-1*Math.floor(a);c=Math.max(Math.min(c,20),0),i=e.toFixed(c)}else i="0";return i},logarithmic:function(e,t,n){var r=e/Math.pow(10,Math.floor(oe.log10(e)));return 0===e?"0":1===r||2===r||5===r||0===t||t===n.length-1?e.toExponential():""}}},Qn=oe.isArray,Zn=oe.isNullOrUndef,er=oe.valueOrDefault,tr=oe.valueAtIndexOrDefault;function nr(e,t){for(var n=[],r=e.length/t,a=0,i=e.length;ac+l)))return o}function ar(e,t){oe.each(e,(function(e){var n,r=e.gc,a=r.length/2;if(a>t){for(n=0;nl)return i;return Math.max(l,1)}function pr(e){var t,n,r=[];for(t=0,n=e.length;t
+ {{ workspace.name }}
+ {{ worksp.name }}
+ All the features with a
+ PRO
+ tag are available in the Pro plan of OpenForm. You can play around and try all Pro features
+ within
+ the form editor, but you can't use them in your real forms. You can subscribe now to gain unlimited access
+ to
+ all our pro features!
+
+ Feel free to contact us if you have any feature request.
+
+ Uploading your file...
+
+
+ or drag and drop
+
+ Up to {{ mbLimit }}mb
+
+ {{ file.file.name }}
+
+ {{ option[displayKey] }}
+
+ Uploading your file...
+
+
+ or drag and drop
+
+ .jpg, .jpeg, .png, .bmp, .gif up to 5mb
+
+ {{ option.name }}
+
+ No option available.
+
+ Success
+
+ This form is protected by a password.
+
+ We disabled the password protection for this form because you are an owner of it.
+
+ You're seeing this because you are an owner of this form.
+ {{ embedCode }}
+
+ {{ formatType(field) }}
+
+ {{ field.name }}
+
+ You need a
+
+
+ {{ form.share_url }}
+
+
+ {{ form.share_url }}
+
+ Input Blocks Text Input Date Input URL Input Phone Input Email Input Checkbox Input Select Input Multi-select Input Number Input File Input Layout Blocks Text Block Page-break Block Divider block Image Block
+ {{ option.name }}
+ {{ option.name }}
+ The code will be injected in the head section of your form page. Click
+ here to get an example CSS code.
+
+ If you already have another form that you like to use as a base for this form, you can do that here.
+ Select another form, confirm, and we will copy all of the other form settings (except the form structure)
+ to this form.
+
+ NEW - our Zapier integration is available for
+ beta testers! During the beta, you don't need a Pro subscription to try it out.
+
+ Operator
+
+ Add some logic to this block. Start by adding some conditions, and then add some actions.
+
+ Select another field/block to copy its logic and apply it to "{{ field.name }}".
+
+ Exclude this field or make it required.
+
+ Change your form field name, pre-fill a value, add hints.
+ No settings found.
+ Exclude this field or make it required.
+
+ If enabled then this field will be star rating input.
+
+ Keep it simple or make it a multi-lines input.
+
+ Adds an end date. This cannot be used with the time option yet.
+
+ Include time. Or not. This cannot be used with the date range option yet.
+
+ Advanced options for your select/multiselect fields.
+ Options won't be in a dropdown anymore, but will all be visible
+ Change your form field name, pre-fill a value, add hints.
+
+ If you enable this, we will hide this field and fill it a unique id (UUID format) on each new form submission
+
+ If you enable this, we will hide this field and fill it a unique number on each new form submission
+
+ {{ col.name }}
+
+ Actions
+
+ No data found.
+
+
+
+
+ Resources
+
+ Community
+
+ Legal
+
+ © Copyright 2022 OpnForm. All rights reserved.
+
+ New in OpnForm
+
+ Click here to see our new features
+
+ Create dynamic links when sharing your form (whether it's embedded or not), that allows you to prefill
+ your form fields. You can use this to personalize the form when sending it to multiple contacts for instance.
+
+ Complete your form below and fill only the fields you want to prefill. You can even leave the required fields empty.
+
+ 100% Free
+
+ You've been paying too much for too long. OpnForm is the first open-source form builder. Need a contact
+ form? Doing a survey? Create a form in 3 minutes and start receiving submissions.
+
+ There are no limits on the number of input fields in your forms. Organize fields and decide which are required.
+
+ You can create as many forms as you need. Forms everywhere, for everything!
+
+ All of you forms can have unlimited responses, no need to worry about quotas and other stressful metrics.
+
+ Receive notifications directly in Slack or in your mailbox whenever your from has a new submission (if you want to).
+
+ You can integrate your form anywhere: on your website, or even within a Notion Page.
+
+ Change form themes, change texts, colors, add images, add custom thank you pages and much more.
+
+ Easily add file upload inputs to your forms. Uploaded files are securely stored for you. Up to 5mb!
+
+ Form logic, URL pre-fill, hidden fields, unique submission id, form password, webhooks, custom code, closing date, etc. It's all there!
+
+ Unfortunately we could not find this form. It may have been deleted by it's author.
+
+
+
+ This form has been seen
+ {{ form.views_count }} time{{ form.views_count > 0 ? 's' : '' }}
+ and it has received
+ {{ form.submissions_count }} submission{{ form.submissions_count > 0 ? 's' : '' }}.
+
+ This form stopped accepting submissions on the {{ displayClosesDate }}
+ This form will stop accepting submissions on the {{ displayClosesDate }}
+
+ The form is now closed because it reached its limit of {{ form.max_submissions_count }} submissions.
+ This form will stop accepting submissions after {{ form.max_submissions_count }} submissions.
+ Share your form using the link below:
+ Embed your form on your website by copying the html code below.
+
+ You can choose between two different URL formats for your form. Be careful, changing your form URL
+ is not a reversible operation. Make sure to udpate your form URL everywhere where it's used.
+ If your users are going to see this url, you might want to make nice and readable. Example:
+ https://opnform.com/forms/contact
+
+ If your user are not going to see your form url (if it's embedded), and if you prefer to have a random
+ non-guessable URL. Example:
+
+ https://opnform.com/forms/b4417f9c-34ae-4421-8006-832ee47786e7
+
+ You don't have any form yet.
+
+ {{ form.title }} - {{
+ form.submissions_count
+ }} Submission{{ form.submissions_count > 0 ? 's' : '' }}
+
+ You have {{ forms.length }} forms
+ ({{ enrichedForms.length }} matching search criteria)
+ .
+
+ You can delete your account. All your data will be removed. This cannot be undone.
+
+
+ And much more!
+
+
+ 🕐
+
+ We're currently updating NotionForms. It shouldn't take more than a minute, please wait.
+ You are receiving this email because you answered the form: slug)}}">"{{$form->title}}".=0&&(o=e),void 0!==i&&(e=n.indexOf(i))>=0&&(s=e),t.minIndex=o,t.maxIndex=s,t.min=n[o],t.max=n[s]},buildTicks:function(){var e=this,t=e._getLabels(),n=e.minIndex,r=e.maxIndex;e.ticks=0===n&&r===t.length-1?t:t.slice(n,r+1)},getLabelForIndex:function(e,t){var n=this,r=n.chart;return r.getDatasetMeta(t).controller._getValueScaleId()===n.id?n.getRightValue(r.data.datasets[t].data[e]):n._getLabels()[e]},_configure:function(){var e=this,t=e.options.offset,n=e.ticks;Mr.prototype._configure.call(e),e.isHorizontal()||(e._reversePixels=!e._reversePixels),n&&(e._startValue=e.minIndex-(t?.5:0),e._valueRange=Math.max(n.length-(t?0:1),1))},getPixelForValue:function(e,t,n){var r,a,i,o=this;return br(t)||br(n)||(e=o.chart.data.datasets[n].data[t]),br(e)||(r=o.isHorizontal()?e.x:e.y),(void 0!==r||void 0!==e&&isNaN(t))&&(a=o._getLabels(),e=oe.valueOrDefault(r,e),t=-1!==(i=a.indexOf(e))?i:t,isNaN(t)&&(t=e)),o.getPixelForDecimal((t-o._startValue)/o._valueRange)},getPixelForTick:function(e){var t=this.ticks;return e<0||e>t.length-1?null:this.getPixelForValue(t[e],e+this.minIndex)},getValueForPixel:function(e){var t=this,n=Math.round(t._startValue+t.getDecimalForPixel(e)*t._valueRange);return Math.min(Math.max(n,0),t.ticks.length-1)},getBasePixel:function(){return this.bottom}}),yr=gr;vr._defaults=yr;var Lr=oe.noop,Ar=oe.isNullOrUndef;function zr(e,t){var n,r,a,i,o=[],s=1e-14,c=e.stepSize,l=c||1,u=e.maxTicks-1,d=e.min,f=e.max,p=e.precision,h=t.min,m=t.max,_=oe.niceNum((m-h)/u/l)*l;if(_u&&(_=oe.niceNum(i*_/u/l)*l),c||Ar(p)?n=Math.pow(10,oe._decimalPlaces(_)):(n=Math.pow(10,p),_=Math.ceil(_*n)/n),r=Math.floor(h/_)*_,a=Math.ceil(m/_)*_,c&&(!Ar(d)&&oe.almostWhole(d/_,_/1e3)&&(r=d),!Ar(f)&&oe.almostWhole(f/_,_/1e3)&&(a=f)),i=(a-r)/_,i=oe.almostEquals(i,Math.round(i),_/1e3)?Math.round(i):Math.ceil(i),r=Math.round(r*n)/n,a=Math.round(a*n)/n,o.push(Ar(d)?r:d);for(var M=1;M0&&r>0&&(e.min=0)}var a=void 0!==t.min||void 0!==t.suggestedMin,i=void 0!==t.max||void 0!==t.suggestedMax;void 0!==t.min?e.min=t.min:void 0!==t.suggestedMin&&(null===e.min?e.min=t.suggestedMin:e.min=Math.min(e.min,t.suggestedMin)),void 0!==t.max?e.max=t.max:void 0!==t.suggestedMax&&(null===e.max?e.max=t.suggestedMax:e.max=Math.max(e.max,t.suggestedMax)),a!==i&&e.min>=e.max&&(a?e.max=e.min+1:e.min=e.max-1),e.min===e.max&&(e.max++,t.beginAtZero||e.min--)},getTickLimit:function(){var e,t=this,n=t.options.ticks,r=n.stepSize,a=n.maxTicksLimit;return r?e=Math.ceil(t.max/r)-Math.floor(t.min/r)+1:(e=t._computeTickLimit(),a=a||11),a&&(e=Math.min(a,e)),e},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Lr,buildTicks:function(){var e=this,t=e.options.ticks,n=e.getTickLimit(),r={maxTicks:n=Math.max(2,n),min:t.min,max:t.max,precision:t.precision,stepSize:oe.valueOrDefault(t.fixedStepSize,t.stepSize)},a=e.ticks=zr(r,e);e.handleDirectionalChanges(),e.max=oe.max(a),e.min=oe.min(a),t.reverse?(a.reverse(),e.start=e.max,e.end=e.min):(e.start=e.min,e.end=e.max)},convertTicksToLabels:function(){var e=this;e.ticksAsNumbers=e.ticks.slice(),e.zeroLineIndex=e.ticks.indexOf(0),Mr.prototype.convertTicksToLabels.call(e)},_configure:function(){var e,t=this,n=t.getTicks(),r=t.min,a=t.max;Mr.prototype._configure.call(t),t.options.offset&&n.length&&(r-=e=(a-r)/Math.max(n.length-1,1)/2,a+=e),t._startValue=r,t._endValue=a,t._valueRange=a-r}}),Tr={position:"left",ticks:{callback:Kn.formatters.linear}},kr=0,xr=1;function Or(e,t,n){var r=[n.type,void 0===t&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===e[r]&&(e[r]={pos:[],neg:[]}),e[r]}function Dr(e,t,n,r){var a,i,o=e.options,s=Or(t,o.stacked,n),c=s.pos,l=s.neg,u=r.length;for(a=0;at.length-1?null:this.getPixelForValue(t[e])}}),Yr=Tr;Nr._defaults=Yr;var Cr=oe.valueOrDefault,Wr=oe.math.log10;function qr(e,t){var n,r,a=[],i=Cr(e.min,Math.pow(10,Math.floor(Wr(t.min)))),o=Math.floor(Wr(t.max)),s=Math.ceil(t.max/Math.pow(10,o));0===i?(n=Math.floor(Wr(t.minNotZero)),r=Math.floor(t.minNotZero/Math.pow(10,n)),a.push(i),i=r*Math.pow(10,n)):(n=Math.floor(Wr(i)),r=Math.floor(i/Math.pow(10,n)));var c=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(i),10==++r&&(r=1,c=++n>=0?1:c),i=Math.round(r*Math.pow(10,n)*c)/c}while(ni.r&&(i.r=u.end,o.r=c),d.start0&&r>0?n:0)},_drawGrid:function(){var e,t,n,r=this,a=r.ctx,i=r.options,o=i.gridLines,s=i.angleLines,c=Hr(s.lineWidth,o.lineWidth),l=Hr(s.color,o.color);if(i.pointLabels.display&&Qr(r),o.display&&oe.each(r.ticks,(function(e,n){0!==n&&(t=r.getDistanceFromCenterForValue(r.ticksAsNumbers[n]),Zr(r,o,t,n))})),s.display&&c&&l){for(a.save(),a.lineWidth=c,a.strokeStyle=l,a.setLineDash&&(a.setLineDash(Rr([s.borderDash,o.borderDash,[]])),a.lineDashOffset=Rr([s.borderDashOffset,o.borderDashOffset,0])),e=r.chart.data.labels.length-1;e>=0;e--)t=r.getDistanceFromCenterForValue(i.ticks.reverse?r.min:r.max),n=r.getPointPosition(e,t),a.beginPath(),a.moveTo(r.xCenter,r.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var e=this,t=e.ctx,n=e.options.ticks;if(n.display){var r,a,i=e.getIndexAngle(0),o=oe.options._parseFont(n),s=Hr(n.fontColor,Q.global.defaultFontColor);t.save(),t.font=o.string,t.translate(e.xCenter,e.yCenter),t.rotate(i),t.textAlign="center",t.textBaseline="middle",oe.each(e.ticks,(function(i,c){(0!==c||n.reverse)&&(r=e.getDistanceFromCenterForValue(e.ticksAsNumbers[c]),n.showLabelBackdrop&&(a=t.measureText(i).width,t.fillStyle=n.backdropColor,t.fillRect(-a/2-n.backdropPaddingX,-r-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),t.fillStyle=s,t.fillText(i,0,-r))})),t.restore()}},_drawTitle:oe.noop}),na=Ir;ta._defaults=na;var ra=oe._deprecated,aa=oe.options.resolve,ia=oe.valueOrDefault,oa=Number.MIN_SAFE_INTEGER||-9007199254740991,sa=Number.MAX_SAFE_INTEGER||9007199254740991,ca={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},la=Object.keys(ca);function ua(e,t){return e-t}function da(e){var t,n,r,a={},i=[];for(t=0,n=e.length;t1?da(h).sort(ua):h.sort(ua),f=Math.min(f,h[0]),p=Math.max(p,h[h.length-1])),f=ba(s,fa(u))||f,p=ba(s,pa(u))||p,f=f===sa?+l.startOf(Date.now(),d):f,p=p===oa?+l.endOf(Date.now(),d)+1:p,s.min=Math.min(f,p),s.max=Math.max(f+1,p),s._table=[],s._timestamps={data:h,datasets:m,labels:_}},buildTicks:function(){var e,t,n,r=this,a=r.min,i=r.max,o=r.options,s=o.ticks,c=o.time,l=r._timestamps,u=[],d=r.getLabelCapacity(a),f=s.source,p=o.distribution;for(l="data"===f||"auto"===f&&"series"===p?l.data:"labels"===f?l.labels:La(r,a,i,d),"ticks"===o.bounds&&l.length&&(a=l[0],i=l[l.length-1]),a=ba(r,fa(o))||a,i=ba(r,pa(o))||i,e=0,t=l.length;e=0;--n)(t=c[n].$filler)&&t.visible&&(a=(r=t.el)._view,i=r._children||[],o=t.mapper,s=a.backgroundColor||Q.global.defaultColor,o&&s&&i.length&&(oe.canvas.clipArea(l,e.chartArea),Pa(l,i,o,a,s,r._loop),oe.canvas.unclipArea(l)))}},Xa=oe.rtl.getRtlAdapter,Ra=oe.noop,Ia=oe.valueOrDefault;function Fa(e,t){return e.usePointStyle&&e.boxWidth>t?t:e.boxWidth}Q._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(e,t){var n=t.datasetIndex,r=this.chart,a=r.getDatasetMeta(n);a.hidden=null===a.hidden?!r.data.datasets[n].hidden:null,r.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(e){var t=e.data.datasets,n=e.options.legend||{},r=n.labels&&n.labels.usePointStyle;return e._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(r?0:void 0);return{text:t[n.index].label,fillStyle:a.backgroundColor,hidden:!e.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(e){var t,n,r,a=document.createElement("ul"),i=e.data.datasets;for(a.setAttribute("class",e.id+"-legend"),t=0,n=i.length;t","
"],col:[2,"
"],tr:[2,"","
"],td:[3,"
"],_default:[0,"",""]};function ve(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&D(e,t)?z.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n","
+
+
+
+
+ OpenForm PRO
+
+
+ We're happy to have you as a Pro customer. If you're having any issue with OpenForm, or if you have a
+ feature request, please contact us.
+
+
+ If you need to collaborate, or to work with multiple workspaces, or just larger file uploads, you can
+ also upgrade our subscription to get an Enterprise subscription.
+
+ We're happy to have you as an Enterprise customer. If you're having any issue with OpenForm, or if you have a
+ feature request, please contact us.
+
+
+ Upload {{ multiple?'file(s)':'a file' }}
+
+
+
+ Upload an image
+
+
+
+
+
+ All your Pro features are de-activated when sharing this form:
+
+
+
+
+
+ About Submission
+
+
+
+
+
+ Custom Code
+
+
+
+
+ Customization
+
+
+
+
+ Error saving your form
+
+
+
+
+
+
+ Information
+
+
+
+ Copy Settings from another form
+
+
+
+ Integrations
+
+
+
+
+ Notifications
+
+
+
+
+ Security & Privacy
+
+
+
+ Form Structure
+
+
+ Logic
+
+
+ 1. Conditions
+
+
+ 2. Actions
+
+
+ Copy logic from another field
+
+
+ Configure "{{ field.name }}" block
+
+
+ General
+
+
+ Customization
+
+
+
+ Configure "{{ field.name }}" block
+
+
+ General
+
+
+ File uploads
+
+
+ Number Options
+
+
+ Text Options
+
+
+ Date Options
+
+
+ Select Options
+
+
+ Customization
+
+
+
+ Advanced Options
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/components/open/tables/components/OpenCheckbox.vue b/resources/js/components/open/tables/components/OpenCheckbox.vue
new file mode 100644
index 000000000..21b2c1ebd
--- /dev/null
+++ b/resources/js/components/open/tables/components/OpenCheckbox.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
diff --git a/resources/js/components/open/tables/components/OpenDate.vue b/resources/js/components/open/tables/components/OpenDate.vue
new file mode 100644
index 000000000..18f9f103d
--- /dev/null
+++ b/resources/js/components/open/tables/components/OpenDate.vue
@@ -0,0 +1,35 @@
+
+
+ {{ value[0] }}
+ to {{ value[1] }}
+
+
+ {{ value }}
+
+
+
+
diff --git a/resources/js/components/open/tables/components/OpenFile.vue b/resources/js/components/open/tables/components/OpenFile.vue
new file mode 100644
index 000000000..2a4e51742
--- /dev/null
+++ b/resources/js/components/open/tables/components/OpenFile.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/components/pages/OpenFormFooter.vue b/resources/js/components/pages/OpenFormFooter.vue
new file mode 100644
index 000000000..2aabd3349
--- /dev/null
+++ b/resources/js/components/pages/OpenFormFooter.vue
@@ -0,0 +1,183 @@
+
+
+ OpnForm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Url Form Prefill
+
+
+
+ How does it work?
+
+
+
+ Your Prefill url
+
+
+
+
+
+ The
+
+ easiest way to create forms for free
+
+
+ Infinite Number of Fields
+
+
+ Infinite Number of Forms
+
+
+ Infinite Responses
+
+
+ Notifications
+
+
+ Integrate Anywhere
+
+
+ Customize Everything
+
+
+ File Uploads
+
+
+ Advanced features
+
+
'
+ Object.keys(responseData.form_cleaning).forEach((key) => {
+ const fieldName = key.charAt(0).toUpperCase() + key.slice(1)
+ let fieldInfo = "
" + fieldName + ""
+ responseData.form_cleaning[key].forEach((msg) => {
+ fieldInfo = fieldInfo + "
"
+ })
+ this.alertWarning(message)
+ } else {
+ this.alertSuccess(responseData.message)
+ }
+ }
+ }
+}
diff --git a/resources/js/pages/auth/login.vue b/resources/js/pages/auth/login.vue
new file mode 100644
index 000000000..1efc876e1
--- /dev/null
+++ b/resources/js/pages/auth/login.vue
@@ -0,0 +1,103 @@
+
+
+ {{ $t('login') }}
+
+
+
+ {{ $t('reset_password') }}
+
+
+
+ {{ $t('reset_password') }}
+
+
+
+ {{ $t('register') }}
+
+
+ {{ $t('register') }}
+
+
+
+
+ {{ $t('verify_email') }}
+
+
+
+ {{ $t('verify_email') }}
+
+
+
+ OpnForm Discount for Students, Academics and NGOs
+
+
+
+
+ {{ $t('page_not_found') }}
+
+
+
+
+
+ {{ form.title }}
+
+
+
+ Form Analytics (last 30 days)
+
+
+ Share/Embed your form
+
+
+
+
+ Share
+
+
+ Embed
+
+
+ Generate new form link
+
+
+ Human Readable URL
+
+
+ Random ID URL
+
+
+ Your Forms
+
+
+ Integrations
+
+
+ Privacy Policy
+
+
+ Terms & Conditions
+
+
+ Your Account
+
+
+
+ Tools
+
+
+ Impersonate User
+
+
+
+
+
+
+
+ Thank you!
+
+
+ We're checking the status of your subscription please wait a moment...
+
+
+ The Open-Source
+
+ Form Builder
+
+ Create beautiful forms and share them anywhere. It takes seconds, you don't need to know how to code
+ and it's free.
+
+
+
+
+ The contact form below is an OpnForm.
+ It can be created for free and it uses open-source code!
+
+