├── public ├── favicon.ico ├── robots.txt ├── mix-manifest.json ├── .htaccess ├── web.config ├── js │ └── app.js.LICENSE.txt └── index.php ├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── database ├── .gitignore ├── seeders │ └── DatabaseSeeder.php ├── migrations │ ├── 2021_08_17_114743_add_followup_to_elections.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2021_02_24_213344_create_countries_table.php │ ├── 2021_02_24_213152_create_uploads_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2014_10_12_200000_add_two_factor_columns_to_users_table.php │ ├── 2021_02_28_222507_create_parties_table.php │ ├── 2021_05_17_130359_create_answers_table.php │ ├── 2021_07_30_183603_add_locale_to_analytics_tables.php │ ├── 2021_05_16_175828_create_questions_table.php │ ├── 2021_05_15_105016_create_elections_table.php │ ├── 2021_05_16_213233_create_election_parties_pivot.php │ └── 2021_05_31_202541_create_statistics_table.php └── factories │ └── UserFactory.php ├── .prettierrc.js ├── resources ├── .DS_Store ├── css │ └── app.css ├── js │ ├── util │ │ ├── cdn.tsx │ │ ├── get-translated-value.tsx │ │ └── create-date-from-mysql.ts │ ├── config │ │ ├── app.ts │ │ └── locales.ts │ ├── pages │ │ ├── dashboard.tsx │ │ ├── auth │ │ │ ├── forgot-password.tsx │ │ │ └── login.tsx │ │ ├── statistic │ │ │ └── election.tsx │ │ └── users │ │ │ └── index.tsx │ ├── components │ │ ├── image │ │ │ └── index.tsx │ │ ├── pass-link.tsx │ │ ├── flags │ │ │ ├── de.tsx │ │ │ ├── sv.tsx │ │ │ ├── fr.tsx │ │ │ ├── fi.tsx │ │ │ ├── world.tsx │ │ │ └── us.tsx │ │ ├── form │ │ │ ├── input.tsx │ │ │ ├── textarea.tsx │ │ │ ├── select.tsx │ │ │ ├── toggle.tsx │ │ │ ├── answer.tsx │ │ │ ├── translations.tsx │ │ │ └── datepicker.tsx │ │ ├── locale-with-flag.tsx │ │ ├── page.tsx │ │ └── locale-switch.tsx │ ├── contexts │ │ └── app.tsx │ ├── app.tsx │ └── types.d.ts ├── views │ ├── emails │ │ └── user │ │ │ └── created.blade.php │ └── app.blade.php └── lang │ └── en │ ├── pagination.php │ ├── auth.php │ └── passwords.php ├── .gitattributes ├── .vscode └── settings.json ├── config ├── translatable.php ├── eloquent-sortable.php ├── cors.php ├── services.php ├── view.php ├── hashing.php ├── broadcasting.php ├── filesystems.php ├── queue.php ├── logging.php ├── cache.php └── mail.php ├── tests ├── TestCase.php ├── Unit │ └── ExampleTest.php ├── Feature │ └── ExampleTest.php └── CreatesApplication.php ├── .styleci.yml ├── .gitignore ├── .editorconfig ├── app ├── Http │ ├── Controllers │ │ ├── Admin │ │ │ ├── DashboardController.php │ │ │ ├── CacheController.php │ │ │ ├── StatisticController.php │ │ │ ├── UserController.php │ │ │ ├── PartyController.php │ │ │ └── CountryController.php │ │ ├── Controller.php │ │ └── Api │ │ │ └── V1 │ │ │ ├── StatisticController.php │ │ │ ├── QuestionController.php │ │ │ ├── PartyController.php │ │ │ └── CountryController.php │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── Authenticate.php │ │ ├── TrustProxies.php │ │ ├── SetLocale.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── HandleInertiaRequests.php │ │ └── LocalizeApi.php │ ├── Requests │ │ ├── StoreAnswers.php │ │ ├── CreateOrEditUser.php │ │ ├── AttachOrSyncParty.php │ │ ├── CreateOrEditCountry.php │ │ ├── CreateOrEditQuestion.php │ │ ├── CreateOrEditParty.php │ │ └── CreateOrEditElection.php │ └── Kernel.php ├── Actions │ └── Fortify │ │ ├── PasswordValidationRules.php │ │ ├── ResetUserPassword.php │ │ ├── CreateNewUser.php │ │ ├── UpdateUserPassword.php │ │ └── UpdateUserProfileInformation.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── EventServiceProvider.php │ ├── RouteServiceProvider.php │ └── FortifyServiceProvider.php ├── Models │ ├── Initiation.php │ ├── Result.php │ ├── Swipe.php │ ├── Answer.php │ ├── User.php │ ├── ElectionParty.php │ ├── Question.php │ ├── Party.php │ ├── Country.php │ ├── Election.php │ └── Upload.php ├── Traits │ └── HasTranslations.php ├── Exceptions │ └── Handler.php ├── Console │ └── Kernel.php ├── Jobs │ ├── InitiateSwiper.php │ ├── CountAnswer.php │ └── SaveResult.php └── Mail │ └── UserInAdminCreated.php ├── webpack.mix.js ├── routes ├── channels.php ├── console.php └── api.php ├── server.php ├── tsconfig.json ├── .env.example ├── phpunit.xml ├── README.md ├── artisan ├── package.json ├── docker-compose.yml ├── composer.json └── .eslintrc.js /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | *.sqlite-journal 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | singleQuote: true 4 | } 5 | -------------------------------------------------------------------------------- /resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOVACT/voteswiper-api/HEAD/resources/.DS_Store -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import "~@tabler/core/dist/css/tabler.css"; 2 | @import "~react-datepicker/dist/react-datepicker.css"; 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": "explicit" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /resources/js/util/cdn.tsx: -------------------------------------------------------------------------------- 1 | import app from '../config/app'; 2 | 3 | const cdn = (fileName: string): string => { 4 | return app.assetsPublicUrl + fileName; 5 | }; 6 | 7 | export default cdn; 8 | -------------------------------------------------------------------------------- /config/translatable.php: -------------------------------------------------------------------------------- 1 | null, 9 | ]; 10 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | $url]) 9 | Login 10 | @endcomponent 11 | 12 | @endcomponent 13 | -------------------------------------------------------------------------------- /app/Http/Controllers/Admin/DashboardController.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/js/util/get-translated-value.tsx: -------------------------------------------------------------------------------- 1 | export const getTranslatedValue = ( 2 | column: TranslatedColumn, 3 | locale: string, 4 | defaultValue?: string 5 | ): string => { 6 | if (column[locale]) { 7 | return column[locale]; 8 | } 9 | 10 | if (defaultValue) { 11 | return defaultValue; 12 | } 13 | 14 | return ''; 15 | }; 16 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | 'sort_order', 8 | 9 | /* 10 | * Define if the models should sort when creating. 11 | * When true, the package will automatically assign the highest order number to a new mode 12 | */ 13 | 'sort_when_creating' => true, 14 | ]; 15 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | get('/'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | Dashboard.layout = (page) => {page}; 15 | 16 | export default Dashboard; 17 | -------------------------------------------------------------------------------- /resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @routes 7 | 8 | 9 | 10 | 11 | @inertia 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | back()->with(['success' => 'The cache has been cleared successfully.']); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 18 | return route('login'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /resources/js/components/image/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cdn from '../../util/cdn'; 3 | 4 | interface Props { 5 | file: Upload; 6 | } 7 | 8 | const Image: React.FC = ({ file }) => { 9 | return ( 10 |
11 | {file.alt_text 18 |
19 | ); 20 | }; 21 | 22 | export default Image; 23 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel applications. By default, we are compiling the CSS 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.ts('resources/js/app.tsx', 'public/js') 15 | .postCss('resources/css/app.css', 'public/css', [ 16 | // 17 | ]); 18 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /resources/js/components/pass-link.tsx: -------------------------------------------------------------------------------- 1 | import { Inertia } from '@inertiajs/inertia'; 2 | import React from 'react'; 3 | 4 | interface Props { 5 | href: string; 6 | } 7 | 8 | export const PassLink: React.FC = ({ children, href }) => { 9 | const visit = ( 10 | event: React.MouseEvent 11 | ): void => { 12 | event.preventDefault(); 13 | Inertia.visit(href); 14 | }; 15 | 16 | return React.cloneElement( 17 | children as React.DetailedReactHTMLElement, 18 | { 19 | onClick: visit, 20 | href, 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /app/Models/Initiation.php: -------------------------------------------------------------------------------- 1 | belongsTo(Election::class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Traits/HasTranslations.php: -------------------------------------------------------------------------------- 1 | getTranslatableAttributes() as $field) { 19 | $attributes[$field] = $this->getTranslation($field, App::getLocale()); 20 | } 21 | return $attributes; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strictNullChecks": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "react", 21 | }, 22 | "include": [ 23 | "resources/js" 24 | ], 25 | "exclude": [ 26 | "node_modules" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\ModelPolicy', 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | $this->registerPolicies(); 27 | 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/js/util/create-date-from-mysql.ts: -------------------------------------------------------------------------------- 1 | const createDateFromMysql = (mysql_string: string): Date | null => { 2 | let t, 3 | result = null; 4 | 5 | if (typeof mysql_string === 'string') { 6 | t = mysql_string.split(/[- :]/); 7 | 8 | //when t[3], t[4] and t[5] are missing they defaults to zero 9 | result = new Date( 10 | Date.UTC( 11 | parseInt(t[0]), 12 | parseInt(t[1]) - 1, 13 | parseInt(t[2]), 14 | parseInt(t[3]) || 0, 15 | parseInt(t[4]) || 0, 16 | parseInt(t[5]) || 0 17 | ) 18 | ); 19 | } 20 | 21 | return result; 22 | }; 23 | 24 | export default createDateFromMysql; 25 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'password' => 'The provided password is incorrect.', 18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 19 | 20 | ]; 21 | -------------------------------------------------------------------------------- /app/Models/Result.php: -------------------------------------------------------------------------------- 1 | 'array', 28 | ]; 29 | 30 | public function election(): BelongsTo 31 | { 32 | return $this->belongsTo(Election::class); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /resources/js/contexts/app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useLocalStorage } from 'react-use'; 3 | 4 | interface AppContext { 5 | drawerOpen: boolean | undefined; 6 | setDrawerOpen: (drawerOpen: boolean) => void; 7 | } 8 | 9 | const AppContext = React.createContext({} as AppContext); 10 | 11 | const AppProvider: React.FC = ({ children }) => { 12 | const [drawerOpen, setDrawerOpen] = useLocalStorage< 13 | AppContext['drawerOpen'] 14 | >('admin:drawer', false); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | const useApp = (): AppContext => React.useContext(AppContext); 24 | 25 | export { AppProvider, useApp }; 26 | -------------------------------------------------------------------------------- /app/Http/Middleware/SetLocale.php: -------------------------------------------------------------------------------- 1 | has('locale')) { 23 | app()->setLocale(session('locale')); 24 | } else { 25 | app()->setLocale(config('app.locale')); 26 | } 27 | return $next($request); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Models/Swipe.php: -------------------------------------------------------------------------------- 1 | belongsTo(Election::class); 29 | } 30 | 31 | public function question(): BelongsTo 32 | { 33 | return $this->belongsTo(Question::class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2021_08_17_114743_add_followup_to_elections.php: -------------------------------------------------------------------------------- 1 | json('followup_link'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('elections', function (Blueprint $table) { 29 | $table->dropColumn('followup_link'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Actions/Fortify/ResetUserPassword.php: -------------------------------------------------------------------------------- 1 | $this->passwordRules(), 24 | ])->validate(); 25 | 26 | $user->forceFill([ 27 | 'password' => Hash::make($input['password']), 28 | ])->save(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | ]; 22 | 23 | /** 24 | * Register any events for your application. 25 | * 26 | * @return void 27 | */ 28 | public function boot() 29 | { 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/js/components/flags/de.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const FlagDE: React.FC> = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Your password has been reset!', 17 | 'sent' => 'We have emailed your password reset link!', 18 | 'throttled' => 'Please wait before retrying.', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that email address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/js/components/flags/sv.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const FlagSV: React.FC> = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 26 | return redirect(RouteServiceProvider::HOME); 27 | } 28 | } 29 | 30 | return $next($request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | reportable(function (Throwable $e) { 38 | // 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreAnswers.php: -------------------------------------------------------------------------------- 1 | election->questions()->get(); 28 | $rules = []; 29 | 30 | foreach($questions as $question) { 31 | $rules['answer_' . $question->id] = 'required|digits_between:0,2'; 32 | $rules['reason_' . $question->id] = 'string||nullable'; 33 | } 34 | 35 | return $rules; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/js/components/flags/fr.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const FlagFR: React.FC> = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 28 | } 29 | 30 | /** 31 | * Register the commands for the application. 32 | * 33 | * @return void 34 | */ 35 | protected function commands() 36 | { 37 | $this->load(__DIR__.'/Commands'); 38 | 39 | require base_path('routes/console.php'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /database/migrations/2021_02_24_213344_create_countries_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('country_code', 10); 19 | $table->string('language_code', 10); 20 | $table->json('name'); 21 | $table->json('slug'); 22 | $table->boolean('published')->default(false); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('countries'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /resources/js/app.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | import { InertiaApp } from '@inertiajs/inertia-react'; 3 | import { InertiaProgress } from '@inertiajs/progress'; 4 | import React from 'react'; 5 | import { render } from 'react-dom'; 6 | import { Helmet } from 'react-helmet'; 7 | import { AppProvider } from './contexts/app'; 8 | 9 | InertiaProgress.init({ 10 | includeCSS: true, 11 | }); 12 | 13 | const el = document.getElementById('app'); 14 | 15 | const App: React.FC = () => { 16 | return ( 17 | 18 | 19 | {el && ( 20 | 23 | require(`./pages/${name}`).default 24 | } 25 | /> 26 | )} 27 | 28 | ); 29 | }; 30 | 31 | render(, el); 32 | -------------------------------------------------------------------------------- /database/migrations/2021_02_24_213152_create_uploads_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->json('filename'); 19 | $table->json('filetype'); 20 | $table->json('width'); 21 | $table->json('height'); 22 | $table->json('filesize'); 23 | $table->json('alt_text')->nullable(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('uploads'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('uuid')->unique(); 19 | $table->text('connection'); 20 | $table->text('queue'); 21 | $table->longText('payload'); 22 | $table->longText('exception'); 23 | $table->timestamp('failed_at')->useCurrent(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('failed_jobs'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /resources/js/components/flags/fi.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const FlagFI: React.FC> = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 26 | 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /app/Models/Answer.php: -------------------------------------------------------------------------------- 1 | belongsTo(ElectionParty::class); 38 | } 39 | 40 | public function question(): BelongsTo 41 | { 42 | return $this->belongsTo(Question::class); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Requests/CreateOrEditUser.php: -------------------------------------------------------------------------------- 1 | 'string|required', 30 | 'email' => [ 31 | 'string', 32 | 'email', 33 | 'required', 34 | $this->user 35 | ? Rule::unique('users', 'email')->ignore($this->user->id) 36 | : 'unique:users,email' 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | 'datetime', 44 | ]; 45 | } 46 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | LOG_LEVEL=debug 9 | 10 | DB_CONNECTION=mysql 11 | DB_HOST=127.0.0.1 12 | DB_PORT=3306 13 | DB_DATABASE=voteswiper_api 14 | DB_USERNAME=root 15 | DB_PASSWORD= 16 | 17 | BROADCAST_DRIVER=log 18 | CACHE_DRIVER=file 19 | QUEUE_CONNECTION=sync 20 | SESSION_DRIVER=file 21 | SESSION_LIFETIME=120 22 | 23 | MEMCACHED_HOST=127.0.0.1 24 | 25 | REDIS_HOST=127.0.0.1 26 | REDIS_PASSWORD=null 27 | REDIS_PORT=6379 28 | 29 | MAIL_MAILER=smtp 30 | MAIL_HOST=mailhog 31 | MAIL_PORT=1025 32 | MAIL_USERNAME=null 33 | MAIL_PASSWORD=null 34 | MAIL_ENCRYPTION=null 35 | MAIL_FROM_ADDRESS=null 36 | MAIL_FROM_NAME="${APP_NAME}" 37 | 38 | AWS_ACCESS_KEY_ID= 39 | AWS_SECRET_ACCESS_KEY= 40 | AWS_DEFAULT_REGION=us-east-1 41 | AWS_BUCKET= 42 | 43 | PUSHER_APP_ID= 44 | PUSHER_APP_KEY= 45 | PUSHER_APP_SECRET= 46 | PUSHER_APP_CLUSTER=mt1 47 | 48 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 49 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 50 | 51 | API_SUBTYPE=voteswiper 52 | API_PREFIX=api 53 | API_NAME=VoteSwiper 54 | -------------------------------------------------------------------------------- /resources/js/components/form/input.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import React from 'react'; 3 | 4 | interface Props 5 | extends React.DetailedHTMLProps< 6 | React.InputHTMLAttributes, 7 | HTMLInputElement 8 | > { 9 | label?: string; 10 | error: boolean; 11 | helperText?: string | React.ReactNode; 12 | } 13 | 14 | const Input: React.FC = ({ label, error, helperText, ...restProps }) => { 15 | return ( 16 |
17 | {label && } 18 |
19 | 24 | {helperText && ( 25 | 26 | {helperText} 27 | 28 | )} 29 |
30 |
31 | ); 32 | }; 33 | 34 | export default Input; 35 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_200000_add_two_factor_columns_to_users_table.php: -------------------------------------------------------------------------------- 1 | text('two_factor_secret') 18 | ->after('password') 19 | ->nullable(); 20 | 21 | $table->text('two_factor_recovery_codes') 22 | ->after('two_factor_secret') 23 | ->nullable(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::table('users', function (Blueprint $table) { 35 | $table->dropColumn('two_factor_secret', 'two_factor_recovery_codes'); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Http/Requests/AttachOrSyncParty.php: -------------------------------------------------------------------------------- 1 | 'boolean|required', 29 | 'playable' => 'boolean|required', 30 | 'program_link' => 'string|url|nullable', 31 | ]; 32 | 33 | $rules['program'] = 'file|nullable|max:15360|mimes:pdf'; 34 | $rules['program_upload_id'] = 'integer|nullable|exists:uploads,id'; 35 | 36 | if (!$this->party) { 37 | $rules['party_id'] = 'integer|required|exists:parties,id'; 38 | } 39 | 40 | return $rules; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Http/Requests/CreateOrEditCountry.php: -------------------------------------------------------------------------------- 1 | 'string|required', 30 | 'country_code' => [ 31 | 'string', 32 | 'required', 33 | $this->country 34 | ? Rule::unique('countries', 'country_code')->ignore($this->country->id) 35 | : 'unique:countries,country_code'], 36 | 'language_code' => 'string|required', 37 | 'published' => 'boolean|required', 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /resources/js/components/form/textarea.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import React from 'react'; 3 | 4 | interface Props 5 | extends React.DetailedHTMLProps< 6 | React.TextareaHTMLAttributes, 7 | HTMLTextAreaElement 8 | > { 9 | label?: string; 10 | error: boolean; 11 | helperText?: string | React.ReactNode; 12 | } 13 | 14 | const Textarea: React.FC = ({ 15 | label, 16 | error, 17 | helperText, 18 | ...restProps 19 | }) => { 20 | return ( 21 |
22 | {label && } 23 |
24 |