Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add dashboard frontend #6

Merged
merged 21 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions .env.ci
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
APP_NAME=Laravel
APP_NAME=solidtime
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_FORCE_HTTPS=false
SESSION_SECURE_COOKIE=false

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=root
DB_CONNECTION=pgsql_test

DB_TEST_HOST=127.0.0.1
DB_TEST_PORT=5432
DB_TEST_DATABASE=laravel
DB_TEST_USERNAME=root
DB_TEST_PASSWORD=root

BROADCAST_DRIVER=log
CACHE_DRIVER=file
Expand Down
10 changes: 9 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ APP_KEY=base64:UNQNf1SXeASNkWux01Rj8EnHYx8FO0kAxWNDwktclkk=
APP_DEBUG=true
APP_URL=https://solidtime.test
APP_FORCE_HTTPS=true
SESSION_SECURE_COOKIE=true

[email protected]

Expand All @@ -12,12 +13,19 @@ LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=pgsql

DB_HOST=pgsql
DB_PORT=5432
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=root

DB_TEST_HOST=pgsql_test
DB_TEST_PORT=5432
DB_TEST_DATABASE=laravel
DB_TEST_USERNAME=root
DB_TEST_PASSWORD=root

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
Expand All @@ -37,7 +45,7 @@ MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
Expand Down
6 changes: 1 addition & 5 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
require("@rushstack/eslint-patch/modern-module-resolution")

module.exports = {
extends: [
'plugin:vue/vue3-essential',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier'
],
extends: ['plugin:vue/vue3-essential', '@vue/eslint-config-typescript/recommended', '@vue/eslint-config-prettier'],
rules: {
'vue/multi-word-component-names': 'off',
}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest

services:
pgsql:
pgsql_test:
image: postgres:15
env:
PGPASSWORD: 'root'
Expand Down
24 changes: 18 additions & 6 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ jobs:
services:
mailpit:
image: 'axllent/mailpit:latest'
pgsql_test:
image: postgres:15
env:
PGPASSWORD: 'root'
POSTGRES_DB: 'laravel'
POSTGRES_USER: 'root'
POSTGRES_PASSWORD: 'root'
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: "Checkout code"
Expand All @@ -24,20 +38,18 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv
coverage: none

- name: Run composer install
run: composer install -n --prefer-dist

- name: Create SQLite database
run: touch database/database.sqlite

- name: Prepare Laravel Application
run: |
cp .env.ci .env
php artisan key:generate
php artisan migrate --seed
php artisan passport:keys

- name: Install dependencies
run: npm ci
Expand All @@ -59,7 +71,7 @@ jobs:
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
name: test-results
path: test-results/
retention-days: 30

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ yarn-error.log
/blob-report/
/playwright/.cache/
/coverage
/extensions/*
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ cp .env.example .env

./vendor/bin/sail artisan migrate:fresh --seed

./vendor/bin/sail php artisan passport:install

./vendor/bin/sail npm install

./vendor/bin/sail npm run build
Expand All @@ -36,6 +38,7 @@ Add the following entry to your `/etc/hosts`
```
127.0.0.1 solidtime.test
127.0.0.1 playwright.solidtime.test
127.0.0.1 mail.solidtime.test
```

## Running E2E Tests
Expand All @@ -52,6 +55,19 @@ npx playwright install
npx playwright codegen solidtime.test
```

## E2E Troubleshooting

If the E2E tests are not working consistently and fail with a timeout during the authentication, you might want to delete the `test-results/.auth` directory to force new test accounts to be created.

## Generate ZOD Client

The Zodius HTTP client is generated using the following command:

```bash

npm run generate:zod
```

## Contributing

This project is in a very early stage. The structure and APIs are still subject to change and not stable.
Expand Down
22 changes: 20 additions & 2 deletions app/Actions/Fortify/CreateNewUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

use App\Models\Organization;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Korridor\LaravelModelValidationRules\Rules\UniqueEloquent;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Laravel\Jetstream\Jetstream;

Expand All @@ -20,12 +23,27 @@ class CreateNewUser implements CreatesNewUsers
* Create a newly registered user.
*
* @param array<string, string> $input
*
* @throws ValidationException
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'name' => [
'required',
'string',
'max:255',
],
'email' => [
'required',
'string',
'email',
'max:255',
new UniqueEloquent(User::class, 'email', function (Builder $builder): Builder {
/** @var Builder<User> $builder */
return $builder->where('is_placeholder', '=', false);
}),
],
'password' => $this->passwordRules(),
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
])->validate();
Expand Down
34 changes: 22 additions & 12 deletions app/Actions/Jetstream/AddOrganizationMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
use App\Models\User;
use Closure;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Korridor\LaravelModelValidationRules\Rules\ExistsEloquent;
use Laravel\Jetstream\Contracts\AddsTeamMembers;
use Laravel\Jetstream\Events\AddingTeamMember;
use Laravel\Jetstream\Events\TeamMemberAdded;
Expand All @@ -21,21 +24,24 @@ class AddOrganizationMember implements AddsTeamMembers
/**
* Add a new team member to the given team.
*/
public function add(User $user, Organization $organization, string $email, ?string $role = null): void
public function add(User $owner, Organization $organization, string $email, ?string $role = null): void
{
Gate::forUser($user)->authorize('addTeamMember', $organization);
Gate::forUser($owner)->authorize('addTeamMember', $organization);

$this->validate($organization, $email, $role);

$newTeamMember = Jetstream::findUserByEmailOrFail($email);
$newOrganizationMember = User::query()
->where('email', $email)
->where('is_placeholder', '=', false)
->firstOrFail();

AddingTeamMember::dispatch($organization, $newTeamMember);
AddingTeamMember::dispatch($organization, $newOrganizationMember);

$organization->users()->attach(
$newTeamMember, ['role' => $role]
$newOrganizationMember, ['role' => $role]
);

TeamMemberAdded::dispatch($organization, $newTeamMember);
TeamMemberAdded::dispatch($organization, $newOrganizationMember);
}

/**
Expand All @@ -46,22 +52,26 @@ protected function validate(Organization $organization, string $email, ?string $
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules(), [
'email.exists' => __('We were unable to find a registered user with this email address.'),
])->after(
], $this->rules())->after(
$this->ensureUserIsNotAlreadyOnTeam($organization, $email)
)->validateWithBag('addTeamMember');
}

/**
* Get the validation rules for adding a team member.
*
* @return array<string, array<Rule|string>>
* @return array<string, array<ValidationRule|Rule|string>>
*/
protected function rules(): array
{
return array_filter([
'email' => ['required', 'email', 'exists:users'],
'email' => [
'required',
'email',
(new ExistsEloquent(User::class, 'email', function (Builder $builder) {
return $builder->where('is_placeholder', '=', false);
}))->withMessage(__('We were unable to find a registered user with this email address.')),
],
'role' => Jetstream::hasRoles()
? ['required', 'string', new Role]
: null,
Expand All @@ -75,7 +85,7 @@ protected function ensureUserIsNotAlreadyOnTeam(Organization $team, string $emai
{
return function ($validator) use ($team, $email) {
$validator->errors()->addIf(
$team->hasUserWithEmail($email),
$team->hasRealUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
Expand Down
11 changes: 5 additions & 6 deletions app/Actions/Jetstream/InviteOrganizationMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function invite(User $user, Organization $organization, string $email, ?s

InvitingTeamMember::dispatch($organization, $email, $role);

/** @var OrganizationInvitation $invitation */
$invitation = $organization->teamInvitations()->create([
'email' => $email,
'role' => $role,
Expand All @@ -50,9 +51,7 @@ protected function validate(Organization $organization, string $email, ?string $
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules($organization), [
'email.unique' => __('This user has already been invited to the team.'),
])->after(
], $this->rules($organization))->after(
$this->ensureUserIsNotAlreadyOnTeam($organization, $email)
)->validateWithBag('addTeamMember');
}
Expand All @@ -68,10 +67,10 @@ protected function rules(Organization $organization): array
'email' => [
'required',
'email',
new UniqueEloquent(OrganizationInvitation::class, 'email', function (Builder $builder) use ($organization) {
(new UniqueEloquent(OrganizationInvitation::class, 'email', function (Builder $builder) use ($organization) {
/** @var Builder<OrganizationInvitation> $builder */
return $builder->whereBelongsTo($organization, 'organization');
}),
}))->withMessage(__('This user has already been invited to the team.')),
],
'role' => Jetstream::hasRoles()
? ['required', 'string', new Role]
Expand All @@ -86,7 +85,7 @@ protected function ensureUserIsNotAlreadyOnTeam(Organization $organization, stri
{
return function ($validator) use ($organization, $email) {
$validator->errors()->addIf(
$organization->hasUserWithEmail($email),
$organization->hasRealUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
Expand Down
Loading
Loading