diff --git a/.env.docker b/.env.docker index 7f70b7e59..6f9948258 100644 --- a/.env.docker +++ b/.env.docker @@ -56,10 +56,6 @@ MIX_LEGAL_TEL="01234 / 56789" TELEGRAM_ADMIN_ID=123456789 TELEGRAM_TOKEN=12345678:abcdefghijklmnop -# ORTS Backend for support tickets -TICKET_HOST=https://example.org -TICKET_APIKEY=xxx - # Should the year in review be visible to the users? YEAR_IN_REVIEW_ACTIVE=false diff --git a/.env.example b/.env.example index 6eb581d90..6d247dc71 100644 --- a/.env.example +++ b/.env.example @@ -63,10 +63,6 @@ MIX_LEGAL_TEL="01234 / 56789" TELEGRAM_ADMIN_ID=123456789 TELEGRAM_TOKEN=12345678:abcdefghijklmnop -# ORTS Backend for support tickets -TICKET_HOST=https://example.org -TICKET_APIKEY=xxx - # Should the year in review be visible to the users? YEAR_IN_REVIEW_BACKEND=false YEAR_IN_REVIEW_ALERT=false diff --git a/.idea/LNKD.tech Editor.xml b/.idea/LNKD.tech Editor.xml new file mode 100644 index 000000000..bf14bba8c --- /dev/null +++ b/.idea/LNKD.tech Editor.xml @@ -0,0 +1,178 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/trwl.iml b/.idea/trwl.iml index 741053ad8..46bae782a 100644 --- a/.idea/trwl.iml +++ b/.idea/trwl.iml @@ -5,7 +5,6 @@ - @@ -191,4 +190,4 @@ - \ No newline at end of file + diff --git a/API_CHANGELOG.md b/API_CHANGELOG.md index 887044181..003563a55 100644 --- a/API_CHANGELOG.md +++ b/API_CHANGELOG.md @@ -4,10 +4,17 @@ In this we try to keep track of changes to the API. Primarily this should document changes that are not backwards compatible or belongs to already documented endpoints. This is to help you keep track of the changes and to help you update your code accordingly. +# 2024-07-17 + +The Endpoint `/report` now correctly uses camelCase for the `subjectType` and `subjectId` field. +Since the current usage of this endpoint is very low, the old snake_case fields will be removed after 2024-08-17. + # 2024-06-28 -The `LeaderboardUserResource` is now returning the whole `LightUserResource` for the user who created it in the `user` field. -Thus the following fields of the `LeaderboardUserResource` are now **marked as deprecated and will be removed after August 2024**. +The `LeaderboardUserResource` is now returning the whole `LightUserResource` for the user who created it in the `user` +field. +Thus the following fields of the `LeaderboardUserResource` are now **marked as deprecated and will be removed after +August 2024**. - `id` - `displayName` @@ -22,8 +29,10 @@ Changed `/operator` to `/operators` ## 2024-05-31 -The `StatusResource` is now returning the whole `LightUserResource` for the user who created it in the `userDetails` field. -Thus the following fields of the `StatusResource` are now **marked as deprecated and will be removed after August 2024**. +The `StatusResource` is now returning the whole `LightUserResource` for the user who created it in the `userDetails` +field. +Thus the following fields of the `StatusResource` are now **marked as deprecated and will be removed after August 2024 +**. - `user` - `username` diff --git a/Dockerfile b/Dockerfile index 79d849e42..e35627daf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ WORKDIR /usr/src/trwl RUN composer install --ignore-platform-reqs --no-interaction --no-progress --no-suggest --optimize-autoloader RUN php artisan optimize -FROM php:8.3.8-apache +FROM php:8.3.9-apache ENV APACHE_DOCUMENT_ROOT=/var/www/html/public RUN apt update && \ diff --git a/app/Dto/Internal/CheckInRequestDto.php b/app/Dto/Internal/CheckInRequestDto.php new file mode 100644 index 000000000..6e69f3ce1 --- /dev/null +++ b/app/Dto/Internal/CheckInRequestDto.php @@ -0,0 +1,105 @@ +travelReason = Business::PRIVATE; + $this->statusVisibility = StatusVisibility::PUBLIC; + $this->body = null; + $this->event = null; + $this->forceFlag = false; + $this->postOnMastodonFlag = false; + $this->chainFlag = false; + } + + public function setUser(Authenticatable $user): CheckInRequestDto { + $this->user = $user; + return $this; + } + + public function setTrip(Trip $trip): CheckInRequestDto { + $this->trip = $trip; + return $this; + } + + public function setOrigin(Station $origin): CheckInRequestDto { + $this->origin = $origin; + return $this; + } + + public function setDeparture(Carbon $departure): CheckInRequestDto { + $this->departure = $departure; + return $this; + } + + public function setDestination(Station $destination): CheckInRequestDto { + $this->destination = $destination; + return $this; + } + + public function setArrival(Carbon $arrival): CheckInRequestDto { + $this->arrival = $arrival; + return $this; + } + + public function setTravelReason(Business $travelReason): CheckInRequestDto { + $this->travelReason = $travelReason; + return $this; + } + + public function setStatusVisibility(StatusVisibility $statusVisibility): CheckInRequestDto { + $this->statusVisibility = $statusVisibility; + return $this; + } + + public function setBody(?string $body): CheckInRequestDto { + $this->body = $body; + return $this; + } + + public function setEvent(?Event $event): CheckInRequestDto { + $this->event = $event; + return $this; + } + + public function setForceFlag(bool $forceFlag): CheckInRequestDto { + $this->forceFlag = $forceFlag; + return $this; + } + + public function setPostOnMastodonFlag(bool $postOnMastodonFlag): CheckInRequestDto { + $this->postOnMastodonFlag = $postOnMastodonFlag; + return $this; + } + + public function setChainFlag(bool $chainFlag): CheckInRequestDto { + $this->chainFlag = $chainFlag; + return $this; + } +} diff --git a/app/Dto/Internal/CheckinSuccessDto.php b/app/Dto/Internal/CheckinSuccessDto.php new file mode 100644 index 000000000..f41a3c787 --- /dev/null +++ b/app/Dto/Internal/CheckinSuccessDto.php @@ -0,0 +1,25 @@ + + */ + public Collection $alsoOnThisConnection; + + public function __construct(Status $status, PointCalculation $pointCalculation, Collection $alsoOnThisConnection) { + $this->status = $status; + $this->pointCalculation = $pointCalculation; + $this->alsoOnThisConnection = $alsoOnThisConnection; + } +} diff --git a/app/Dto/LivePointDto.php b/app/Dto/LivePointDto.php index 4300ba741..d15146569 100644 --- a/app/Dto/LivePointDto.php +++ b/app/Dto/LivePointDto.php @@ -69,7 +69,7 @@ * @OA\Property( * title="statusId", * description="ID of status", - * format="int64", + * format="int", * example=12345 * ) **/ diff --git a/app/Dto/MentionDto.php b/app/Dto/MentionDto.php index bc7d6a83b..a97922d31 100644 --- a/app/Dto/MentionDto.php +++ b/app/Dto/MentionDto.php @@ -27,7 +27,7 @@ /** * @OA\Property( * title="position", - * format="int64", + * format="int", * example=0 * ) */ diff --git a/app/Enum/HafasTravelType.php b/app/Enum/HafasTravelType.php index 0c47901fe..da3649617 100644 --- a/app/Enum/HafasTravelType.php +++ b/app/Enum/HafasTravelType.php @@ -29,16 +29,17 @@ enum HafasTravelType: string public function getEmoji(): string { return match ($this->value) { - 'nationalExpress', 'national' => '🚄', - 'regionalExp', 'regional' => '🚆', - 'suburban' => '🚋', - 'bus' => '🚌', - 'ferry' => '⛴', - 'subway' => '🚇', - 'tram' => '🚊', - 'taxi' => '🚖', - 'plane' => '✈️', - default => '', + 'nationalExpress' => '🚄', + 'regionalExp', 'national' => '🚆', + 'regional' => '🚞', + 'suburban' => '🚋', + 'bus' => '🚌', + 'ferry' => '⛴', + 'subway' => '🚇', + 'tram' => '🚊', + 'taxi' => '🚖', + 'plane' => '✈️', + default => '', }; } diff --git a/app/Enum/PointReason.php b/app/Enum/PointReason.php index e965e7dbd..71fdd0731 100644 --- a/app/Enum/PointReason.php +++ b/app/Enum/PointReason.php @@ -6,9 +6,9 @@ /** * @OA\Schema( * title="PointsReason", - * description="What is the reason for the points calculation factor? (0=in time => 100%, 1=good enough => 25%, 2=not sufficient (1 point), 3=forced => no points)", + * description="What is the reason for the points calculation factor? (0=in time => 100%, 1=good enough => 25%, 2=not sufficient (1 point), 3=forced => no points, 4=manual trip => no points, 5=points disabled)", * type="integer", - * enum={0,1,2,3}, + * enum={0,1,2,3,4,5}, * example=1 * ) */ @@ -23,4 +23,6 @@ enum PointReason: int * Trip was manually created by the user => no points. */ case MANUAL_TRIP = 4; + + case POINTS_DISABLED = 5; } diff --git a/app/Enum/Report/ReportableSubject.php b/app/Enum/Report/ReportableSubject.php index 1479a8547..90d9f1fa8 100644 --- a/app/Enum/Report/ReportableSubject.php +++ b/app/Enum/Report/ReportableSubject.php @@ -7,4 +7,5 @@ enum ReportableSubject: string case EVENT = 'Event'; case STATUS = 'Status'; case USER = 'User'; + case TRIP = 'Trip'; } diff --git a/app/Http/Controllers/API/v1/Controller.php b/app/Http/Controllers/API/v1/Controller.php index ad4ccc28e..484fe166a 100644 --- a/app/Http/Controllers/API/v1/Controller.php +++ b/app/Http/Controllers/API/v1/Controller.php @@ -89,6 +89,10 @@ * name="Webhooks", * description="Manage Webhooks for third party applications" * ) + * @OA\Tag( + * name="Report", + * description="Report a Status, Event or User to the admins" + * ) */ class Controller extends \App\Http\Controllers\Controller { diff --git a/app/Http/Controllers/API/v1/EventController.php b/app/Http/Controllers/API/v1/EventController.php index f286afef3..83e083851 100644 --- a/app/Http/Controllers/API/v1/EventController.php +++ b/app/Http/Controllers/API/v1/EventController.php @@ -159,6 +159,7 @@ public static function statuses(string $slug): AnonymousResourceCollection { /** * @OA\Get( * path="/events", + * operationId="getEvents", * tags={"Events"}, * summary="[Auth optional] Show active or upcoming events for the given timestamp", * description="Returns all active or upcoming events for the given timestamp. Default timestamp is now. If upcoming is set to true, all events ending after the timestamp are returned.", @@ -275,6 +276,7 @@ public function suggest(Request $request): JsonResponse { /** * @OA\Get( * path="/activeEvents", + * operationId="getActiveEvents", * tags={"Events"}, * summary="DEPRECATED - USE /events - removed after 2024-08", * description="DEPRECATED - USE /events - removed after 2024-08", diff --git a/app/Http/Controllers/API/v1/FollowController.php b/app/Http/Controllers/API/v1/FollowController.php index 06c4e4abe..432db161f 100644 --- a/app/Http/Controllers/API/v1/FollowController.php +++ b/app/Http/Controllers/API/v1/FollowController.php @@ -226,7 +226,7 @@ public function getFollowings(): AnonymousResourceCollection { * @OA\Property( * property="userId", * title="userId", - * format="int64", + * format="int", * description="ID of the to-be-unfollowed user", * example=1 * ) @@ -283,7 +283,7 @@ public function removeFollower(Request $request): JsonResponse { * @OA\Property( * property="userId", * title="userId", - * format="int64", + * format="int", * description="ID of the user who sent the follow request", * example=1 * ) @@ -336,7 +336,7 @@ public function approveFollowRequest(Request $request): JsonResponse { * @OA\Property( * property="userId", * title="userId", - * format="int64", + * format="int", * description="ID of the user who sent the follow request", * example=1 * ) diff --git a/app/Http/Controllers/API/v1/PrivacyPolicyController.php b/app/Http/Controllers/API/v1/PrivacyPolicyController.php index de3049a86..68acc1bb3 100644 --- a/app/Http/Controllers/API/v1/PrivacyPolicyController.php +++ b/app/Http/Controllers/API/v1/PrivacyPolicyController.php @@ -39,6 +39,7 @@ public function getPrivacyPolicy(): PrivacyPolicyResource { /** * @OA\Post( * path="/settings/acceptPrivacy", + * operationId="acceptPrivacyPolicy", * tags={"Settings"}, * summary="Accept the current privacy policy", * description="Accept the current privacy policy", diff --git a/app/Http/Controllers/API/v1/ReportController.php b/app/Http/Controllers/API/v1/ReportController.php index 3f382d58a..d7b4f3ddf 100644 --- a/app/Http/Controllers/API/v1/ReportController.php +++ b/app/Http/Controllers/API/v1/ReportController.php @@ -6,12 +6,11 @@ use App\Enum\Report\ReportReason; use App\Enum\Report\ReportStatus; use App\Models\Report; +use App\Repositories\ReportRepository; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; -use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Http; use Illuminate\Validation\Rules\Enum; class ReportController extends Controller @@ -19,15 +18,16 @@ class ReportController extends Controller /** * @OA\Post( * path="/report", + * operationId="report", * summary="Report a Status, Event or User to the admins.", - * tags={"User", "Status", "Events"}, + * tags={"Report"}, * security={{"passport": {}}, {"token": {}}}, * @OA\RequestBody( * required=true, * @OA\JsonContent( - * required={"subject_type", "subject_id", "reason"}, - * @OA\Property(property="subject_type", type="string", enum={"Event", "Status", "User"}, example="Status"), - * @OA\Property(property="subject_id", type="integer", example=1), + * required={"subjectType", "subjectId", "reason"}, + * @OA\Property(property="subjectType", type="string", enum={"Event", "Status", "User"}, example="Status"), + * @OA\Property(property="subjectId", type="integer", example=1), * @OA\Property(property="reason", type="string", enum={"inappropriate", "implausible", "spam", "illegal", "other"}, example="inappropriate"), * @OA\Property(property="description", type="string", example="The status is inappropriate because...", nullable=true), * ), @@ -43,31 +43,24 @@ class ReportController extends Controller */ public function store(Request $request): Response { $validated = $request->validate([ - 'subject_type' => ['required', new Enum(ReportableSubject::class)], - 'subject_id' => ['required', 'integer', 'min:1'], + 'subject_type' => [new Enum(ReportableSubject::class)], // Todo: Remove after 2023-08-17 + 'subjectType' => ['required_without:subject_type', new Enum(ReportableSubject::class)], + 'subjectId' => ['required_without:subject_id', 'integer', 'min:1'], + 'subject_id' => ['integer', 'min:1'], // Todo: Remove after 2023-08-17 'reason' => ['required', new Enum(ReportReason::class)], 'description' => ['nullable', 'string'], ]); - $report = Report::create([ - 'subject_type' => 'App\\Models\\' . $validated['subject_type'], - 'subject_id' => $validated['subject_id'], - 'reason' => $validated['reason'], - 'description' => $validated['description'], - 'reporter_id' => auth()->id(), - ]); + $subjectType = $validated['subjectType'] ?? $validated['subject_type']; // Todo: Remove after 2023-08-17 + $subjectId = $validated['subjectId'] ?? $validated['subject_id']; // Todo: Remove after 2023-08-17 - if (!App::runningUnitTests() && config('app.admin.notification.url') !== null) { - Http::post(config('app.admin.notification.url'), [ - 'chat_id' => config('app.admin.notification.chat_id'), - 'text' => "🚨 New Report for " . $validated['subject_type'] . "" . PHP_EOL - . "Reason: " . $validated['reason'] . PHP_EOL - . "Description: " . ($validated['description'] ?? 'None') . PHP_EOL - . "View Report: " . config('app.url') . "/admin/reports/" . $report->id . PHP_EOL - , - 'parse_mode' => 'HTML', - ]); - } + (new ReportRepository())->createReport( + subjectType: ReportableSubject::from($subjectType), + subjectId: $subjectId, + reason: ReportReason::from($validated['reason']), + description: $validated['description'], + reporter: auth()->user() + ); return response()->noContent(201); } diff --git a/app/Http/Controllers/API/v1/SettingsController.php b/app/Http/Controllers/API/v1/SettingsController.php index 3f9e30293..61eff27e7 100644 --- a/app/Http/Controllers/API/v1/SettingsController.php +++ b/app/Http/Controllers/API/v1/SettingsController.php @@ -21,6 +21,7 @@ class SettingsController extends Controller /** * @OA\Get( * path="/settings/profile", + * operationId="getProfileSettings", * tags={"Settings"}, * summary="Get the current user's profile settings", * description="Get the current user's profile settings", @@ -65,36 +66,60 @@ public function updateMail(Request $request): UserProfileSettingsResource|JsonRe /** * @OA\Put( - * path="/settings/profile", - * operationId="putProfileSettings", - * tags={"Settings"}, - * summary="Update the current user's profile settings", - * @OA\RequestBody( - * required=true, - * @OA\JsonContent( - * @OA\Property(property="username", type="string", example="Gertrud123", maxLength=25), - * @OA\Property(property="displayName", type="string", example="Gertrud", maxLength=50), - * @OA\Property(property="privateProfile", type="boolean", example=false, nullable=true), - * @OA\Property(property="preventIndex", type="boolean", example=false, nullable=true), - * @OA\Property(property="privacyHideDays", type="integer", example=1, nullable=true), - * @OA\Property(property="defaultStatusVisibility", type="integer", nullable=true, @OA\Schema(ref="#/components/schemas/StatusVisibility")), - * @OA\Property(property="mastodonVisibility", type="integer", nullable=true, @OA\Schema(ref="#/components/schemas/MastodonVisibility")), - * @OA\Property(property="mapProvider", type="string", nullable=true, @OA\Schema(ref="#/components/schemas/MapProvider"), example="cargo"), - * @OA\Property(property="friendCheckin", type="string", nullable=true, @OA\Schema(ref="#/components/schemas/FriendCheckinSetting"), example="forbidden") - * ) - * ), - * @OA\Response( - * response=200, - * description="Success", - * @OA\JsonContent( - * @OA\Property(property="data", type="object", ref="#/components/schemas/UserProfileSettings") - * ) - * ), - * @OA\Response(response=401, description="Unauthorized"), - * @OA\Response(response=422, description="Unprocessable Entity"), - * @OA\Response(response=400, description="Bad Request"), - * security={{"passport": {"write-settings"}}, {"token": {}}} - * ) + * path="/settings/profile", + * operationId="updateProfileSettings", + * tags={"Settings"}, + * summary="Update the current user's profile settings", + * description="Update the current user's profile settings", + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * @OA\Property(property="username", type="string", example="gertrud123", maxLength=25), + * @OA\Property(property="displayName", type="string", example="Gertrud", maxLength=50), + * @OA\Property(property="privateProfile", type="boolean", example=false, nullable=true), + * @OA\Property(property="preventIndex", type="boolean", example=false, nullable=true), + * @OA\Property(property="privacyHideDays", type="integer", example=1, nullable=true), + * @OA\Property( + * property="defaultStatusVisibility", + * type="integer", + * nullable=true, + * @OA\Schema(ref="#/components/schemas/StatusVisibility") + * ), + * @OA\Property( + * property="mastodonVisibility", + * type="integer", + * nullable=true, + * @OA\Schema(ref="#/components/schemas/MastodonVisibility") + * ), + * @OA\Property( + * property="mapProvider", + * type="string", + * nullable=true, + * @OA\Schema(ref="#/components/schemas/MapProvider") + * ), + * @OA\Property( + * property="friendCheckin", + * type="string", + * nullable=true, + * @OA\Schema(ref="#/components/schemas/FriendCheckinSetting"), + * example="forbidden" + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\JsonContent( + * @OA\Property(property="data", type="object", ref="#/components/schemas/UserProfileSettings") + * ) + * ), + * @OA\Response(response=401, description="Unauthorized"), + * @OA\Response(response=422, description="Unprocessable Entity"), + * @OA\Response(response=400, description="Bad Request"), + * security={ + * {"passport": {"write-settings"}}, {"token": {}} + * } + * ) */ public function updateSettings(Request $request): UserProfileSettingsResource|JsonResponse { $validated = $request->validate([ diff --git a/app/Http/Controllers/API/v1/StatusController.php b/app/Http/Controllers/API/v1/StatusController.php index 84fc9c44e..90736dd01 100644 --- a/app/Http/Controllers/API/v1/StatusController.php +++ b/app/Http/Controllers/API/v1/StatusController.php @@ -29,6 +29,7 @@ use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\ValidationException; use InvalidArgumentException; +use OpenApi\Annotations as OA; class StatusController extends Controller { @@ -561,9 +562,11 @@ public function getPolyline(string $parameters): JsonResource { * description="successful operation", * @OA\JsonContent( * @OA\Property ( - * property="data", - * type="object", - * ref="#/components/schemas/Stopovers" + * property="data", type="object", + * @OA\Property( + * property="1", type="array", description="Array of stopovers. Key describes trip id", + * @OA\Items(ref="#/components/schemas/StopoverResource") + * ) * ) * ) * ), diff --git a/app/Http/Controllers/API/v1/SupportController.php b/app/Http/Controllers/API/v1/SupportController.php deleted file mode 100644 index ce7cab0be..000000000 --- a/app/Http/Controllers/API/v1/SupportController.php +++ /dev/null @@ -1,38 +0,0 @@ -validate([ - 'subject' => ['required', 'string', 'max:255'], - 'message' => ['required', 'string',] - ]); - - try { - $ticketNumber = TicketController::createTicket( - user: auth()->user(), - subject: $validated['subject'], - message: $validated['message'], - ); - return $this->sendResponse(['ticket' => $ticketNumber], 201); - } catch (GuzzleException $exception) { - report($exception); - return $this->sendError(null, 503); - } catch (RateLimitExceededException) { - return $this->sendError(__('support.rate_limit_exceeded'), 429); - } - } -} diff --git a/app/Http/Controllers/API/v1/TransportController.php b/app/Http/Controllers/API/v1/TransportController.php index 317127261..892c4a499 100644 --- a/app/Http/Controllers/API/v1/TransportController.php +++ b/app/Http/Controllers/API/v1/TransportController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\API\v1; +use App\Dto\Internal\CheckinSuccessDto; use App\Dto\Transport\Station as StationDto; use App\Enum\Business; use App\Enum\StatusVisibility; @@ -15,9 +16,11 @@ use App\Http\Controllers\Backend\Transport\TrainCheckinController; use App\Http\Controllers\HafasController; use App\Http\Controllers\TransportController as TransportBackend; +use App\Http\Resources\CheckinSuccessResource; use App\Http\Resources\StationResource; use App\Http\Resources\StatusResource; use App\Http\Resources\TripResource; +use App\Hydrators\CheckinRequestHydrator; use App\Models\Event; use App\Models\Station; use App\Models\User; @@ -217,16 +220,16 @@ public function getDepartures(Request $request, int $stationId): JsonResponse { * description="successful operation", * @OA\JsonContent( * @OA\Property(property="data", type="object", - * @OA\Property(property="id", type="int64", example=1), + * @OA\Property(property="id", type="int", example=1), * @OA\Property(property="category", ref="#/components/schemas/HafasTravelType"), * @OA\Property(property="number", type="string", example="4-a6s4-4"), * @OA\Property(property="lineName", type="string", example="S 4"), - * @OA\Property(property="journeyNumber", type="int64", example="34427"), + * @OA\Property(property="journeyNumber", type="int", example="34427"), * @OA\Property(property="origin", ref="#/components/schemas/Station"), * @OA\Property(property="destination", ref="#/components/schemas/Station"), * @OA\Property(property="stopovers", type="array", * @OA\Items( - * ref="#/components/schemas/Stopover" + * ref="#/components/schemas/StopoverResource" * ) * ), * ) @@ -340,7 +343,7 @@ public function getNextStationByCoordinates(Request $request): JsonResponse { * @OA\Response( * response=201, * description="successful operation", - * @OA\JsonContent(ref="#/components/schemas/CheckinResponse") + * @OA\JsonContent(ref="#/components/schemas/CheckinSuccessResource") * ), * @OA\Response(response=400, description="Bad request"), * @OA\Response(response=401, description="Unauthorized"), @@ -354,7 +357,6 @@ public function getNextStationByCoordinates(Request $request): JsonResponse { * @param Request $request * * @return JsonResponse - * @throws NotConnectedException */ public function create(Request $request): JsonResponse { $validated = $request->validate([ @@ -384,46 +386,9 @@ public function create(Request $request): JsonResponse { } try { - $searchKey = empty($validated['ibnr']) ? 'id' : 'ibnr'; - $trip = HafasController::getHafasTrip($validated['tripId'], $validated['lineName']); - $originStation = Station::where($searchKey, $validated['start'])->first(); - $departure = Carbon::parse($validated['departure']); - $destinationStation = Station::where($searchKey, $validated['destination'])->first(); - $arrival = Carbon::parse($validated['arrival']); - $travelReason = Business::tryFrom($validated['business'] ?? Business::PRIVATE->value); - $event = isset($validated['eventId']) ? Event::find($validated['eventId']) : null; - - // check in the authenticated user - $checkinResponse = TrainCheckinController::checkin( - user: Auth::user(), - trip: $trip, - origin: $originStation, - departure: $departure, - destination: $destinationStation, - arrival: $arrival, - travelReason: $travelReason, - visibility: StatusVisibility::tryFrom($validated['visibility'] ?? StatusVisibility::PUBLIC->value), - body: $validated['body'] ?? null, - event: $event, - force: isset($validated['force']) && $validated['force'], - postOnMastodon: isset($validated['toot']) && $validated['toot'], - shouldChain: isset($validated['chainPost']) && $validated['chainPost'] - ); - $checkinResponse['status'] = new StatusResource($checkinResponse['status']); - - //Rewrite ['points'] so the DTO will match the documented structure -> non-breaking api change - $pointsCalculation = $checkinResponse['points']; - $checkinResponse['points'] = [ - 'points' => $pointsCalculation->points, - 'calculation' => [ - 'base' => $pointsCalculation->basePoints, - 'distance' => $pointsCalculation->distancePoints, - 'factor' => $pointsCalculation->factor, - 'reason' => $pointsCalculation->reason->value, - ], - 'additional' => null, //unused old attribute (not removed so this isn't breaking) - ]; + $checkinResponse = TrainCheckinController::checkin((new CheckinRequestHydrator($validated))->hydrateFromApi()); + //ToDo: Check if documented structure has changed // if isset, check in the other users with their default values foreach ($withUsers ?? [] as $user) { $checkin = TrainCheckinController::checkin( @@ -440,7 +405,7 @@ public function create(Request $request): JsonResponse { $user->notify(new YouHaveBeenCheckedIn($checkin['status'], auth()->user())); } - return $this->sendResponse($checkinResponse, 201); //ToDo: Check if documented structure has changed + return $this->sendResponse(new CheckinSuccessResource($checkinResponse), 201); } catch (CheckInCollisionException $exception) { return $this->sendError([ 'status_id' => $exception->checkin->status_id, diff --git a/app/Http/Controllers/API/v1/TripController.php b/app/Http/Controllers/API/v1/TripController.php index 1f0213a9c..8f20c2978 100644 --- a/app/Http/Controllers/API/v1/TripController.php +++ b/app/Http/Controllers/API/v1/TripController.php @@ -10,7 +10,9 @@ use App\Models\HafasOperator; use App\Models\Station; use Carbon\Carbon; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rules\Enum; @@ -22,15 +24,18 @@ class TripController extends Controller * * @param Request $request * - * @return TripResource + * @return TripResource|Response * * @todo add docs * @todo currently the stations need to be in the database. We need to add a fallback to HAFAS. * -> later solve the problem for non-existing stations */ - public function createTrip(Request $request): TripResource { + public function createTrip(Request $request): TripResource|JsonResponse { if (!auth()->user()?->can('create-manual-trip')) { - abort(403, 'this endpoint is currently only available for beta users'); + return response()->json(['message' => 'This endpoint is currently only available for open-beta users (you can enable open beta in your settings).'], 403); + } + if (auth()->user()?->can('disallow-manual-trips')) { + return response()->json(['message' => 'You are not allowed to create manual trips'], 403); } $validated = $request->validate( diff --git a/app/Http/Controllers/API/v1/UserController.php b/app/Http/Controllers/API/v1/UserController.php index dff65847d..aef373b80 100644 --- a/app/Http/Controllers/API/v1/UserController.php +++ b/app/Http/Controllers/API/v1/UserController.php @@ -203,7 +203,7 @@ public function show(string $username): UserResource { * @OA\Property( * property="userId", * title="userId", - * format="int64", + * format="int", * description="ID of the to-be-blocked user", * example=1 * ) @@ -267,7 +267,7 @@ public function createBlock(int $userId): JsonResponse { * @OA\Property( * property="userId", * title="userId", - * format="int64", + * format="int", * description="ID of the to-be-unblocked user", * example=1 * ) diff --git a/app/Http/Controllers/Backend/LeaderboardController.php b/app/Http/Controllers/Backend/LeaderboardController.php index 01f223c36..2d2c74d0f 100644 --- a/app/Http/Controllers/Backend/LeaderboardController.php +++ b/app/Http/Controllers/Backend/LeaderboardController.php @@ -20,6 +20,10 @@ public static function getLeaderboard( int $limit = 20, bool $onlyFollowings = false ): Collection { + if (auth()->user()?->points_enabled === false) { + return collect(); + } + if ($since == null) { $since = now()->subWeek(); } @@ -79,6 +83,11 @@ public static function getLeaderboard( } public static function getMonthlyLeaderboard(Carbon $date): Collection { + if (auth()->user()?->points_enabled === false) { + return collect(); + } + + $data = DB::table('statuses') ->join('train_checkins', 'train_checkins.status_id', '=', 'statuses.id') ->join('users', 'statuses.user_id', '=', 'users.id') diff --git a/app/Http/Controllers/Backend/Social/MastodonController.php b/app/Http/Controllers/Backend/Social/MastodonController.php index fba718139..ca3dd8c5a 100644 --- a/app/Http/Controllers/Backend/Social/MastodonController.php +++ b/app/Http/Controllers/Backend/Social/MastodonController.php @@ -94,7 +94,8 @@ private static function createMastodonServer(string $domain): MastodonServer { $info = Mastodon::domain($domain)->createApp( client_name: config('trwl.mastodon_appname'), redirect_uris: config('trwl.mastodon_redirect'), - scopes: 'write read' + scopes: 'write read', + website: config('app.url') ); return MastodonServer::updateOrCreate([ 'domain' => $domain, diff --git a/app/Http/Controllers/Backend/Support/TicketController.php b/app/Http/Controllers/Backend/Support/TicketController.php deleted file mode 100644 index d08dfb491..000000000 --- a/app/Http/Controllers/Backend/Support/TicketController.php +++ /dev/null @@ -1,52 +0,0 @@ -email === null || $user->email_verified_at === null) { - throw new InvalidArgumentException('E-Mail address is missing.'); - } - - if (RateLimiter::tooManyAttempts('create-ticket:' . $user->id, 1)) { - throw new RateLimitExceededException(); - } - RateLimiter::hit('create-ticket:' . $user->id); - - $client = new Client(['base_uri' => config('ticket.host')]); - $result = $client->post('/api/tickets.json', [ - 'json' => [ - 'name' => $user->username, - 'email' => $user->email, - 'subject' => $subject, - 'message' => $message, - ], - 'headers' => [ - 'X-API-Key' => config('ticket.api_key'), - ], - ]); - return (int) $result->getBody()->getContents(); - - - } -} diff --git a/app/Http/Controllers/Backend/Transport/PointsCalculationController.php b/app/Http/Controllers/Backend/Transport/PointsCalculationController.php index 3feb943f5..e0ea02f8f 100644 --- a/app/Http/Controllers/Backend/Transport/PointsCalculationController.php +++ b/app/Http/Controllers/Backend/Transport/PointsCalculationController.php @@ -28,6 +28,10 @@ public static function calculatePoints( bool $forceCheckin = false, Carbon $timestampOfView = null ): PointCalculation { + if (auth()->user()?->points_enabled === false) { + return self::returnZeroPoints(); + } + if ($timestampOfView == null) { $timestampOfView = now(); } @@ -59,6 +63,16 @@ private static function calculatePointsWithReason( ); } + private static function returnZeroPoints(): PointCalculation { + return new PointCalculation( + points: 0, + basePoints: 0, + distancePoints: 0, + reason: PointReason::POINTS_DISABLED, + factor: 0, + ); + } + public static function getPointsByReason(PointReason $pointReason, int $points, float $factor): int { if (in_array($pointReason, self::REDUCED_POINTS)) { return $pointReason === PointReason::MANUAL_TRIP ? 0 : 1; diff --git a/app/Http/Controllers/Backend/Transport/TrainCheckinController.php b/app/Http/Controllers/Backend/Transport/TrainCheckinController.php index 47ec096c0..ade26cab0 100644 --- a/app/Http/Controllers/Backend/Transport/TrainCheckinController.php +++ b/app/Http/Controllers/Backend/Transport/TrainCheckinController.php @@ -2,10 +2,9 @@ namespace App\Http\Controllers\Backend\Transport; -use App\Dto\PointCalculation; -use App\Enum\Business; +use App\Dto\Internal\CheckInRequestDto; +use App\Dto\Internal\CheckinSuccessDto; use App\Enum\PointReason; -use App\Enum\StatusVisibility; use App\Events\StatusUpdateEvent; use App\Events\UserCheckedIn; use App\Exceptions\Checkin\AlreadyCheckedInException; @@ -15,27 +14,22 @@ use App\Exceptions\StationNotOnTripException; use App\Http\Controllers\Backend\Support\LocationController; use App\Http\Controllers\Controller; -use App\Http\Controllers\HafasController; use App\Http\Controllers\StatusController as StatusBackend; use App\Http\Controllers\TransportController; -use App\Http\Resources\StatusResource; use App\Jobs\RefreshStopover; use App\Models\Checkin; -use App\Models\Event; use App\Models\Station; use App\Models\Status; use App\Models\Stopover; use App\Models\Trip; -use App\Models\User; use App\Notifications\UserJoinedConnection; +use App\Repositories\CheckinHydratorRepository; use Carbon\Carbon; use Exception; use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Http\Resources\Json\AnonymousResourceCollection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use InvalidArgumentException; -use JetBrains\PhpStorm\ArrayShape; use PDOException; abstract class TrainCheckinController extends Controller @@ -46,53 +40,34 @@ abstract class TrainCheckinController extends Controller * @throws CheckInCollisionException * @throws AlreadyCheckedInException */ - #[ArrayShape([ - 'status' => Status::class, - 'points' => PointCalculation::class, - 'alsoOnThisConnection' => AnonymousResourceCollection::class - ])] - public static function checkin( - User $user, - Trip $trip, - Station $origin, - Carbon $departure, - Station $destination, - Carbon $arrival, - Business $travelReason = Business::PRIVATE, - StatusVisibility $visibility = StatusVisibility::PUBLIC, - ?string $body = null, - ?Event $event = null, - bool $force = false, - bool $postOnMastodon = false, - bool $shouldChain = false - ): array { - if ($departure->isAfter($arrival)) { + public static function checkin(CheckInRequestDto $dto): CheckinSuccessDto { + if ($dto->departure->isAfter($dto->arrival)) { throw new InvalidArgumentException('Departure time must be before arrival time'); } try { $status = StatusBackend::createStatus( - user: $user, - business: $travelReason, - visibility: $visibility, - body: $body, - event: $event + user: $dto->user, + business: $dto->travelReason, + visibility: $dto->statusVisibility, + body: $dto->body, + event: $dto->event ); $checkinResponse = self::createCheckin( status: $status, - trip: $trip, - origin: $origin, - destination: $destination, - departure: $departure, - arrival: $arrival, - force: $force, + trip: $dto->trip, + origin: $dto->origin, + destination: $dto->destination, + departure: $dto->departure, + arrival: $dto->arrival, + force: $dto->forceFlag, ); UserCheckedIn::dispatch( $status, - $postOnMastodon && $user->socialProfile?->mastodon_id !== null, - $shouldChain + $dto->postOnMastodonFlag && $dto->user->socialProfile?->mastodon_id !== null, + $dto->chainFlag ); return $checkinResponse; @@ -119,11 +94,6 @@ public static function checkin( * @throws ModelNotFoundException * @throws AlreadyCheckedInException */ - #[ArrayShape([ - 'status' => Status::class, - 'points' => PointCalculation::class, - 'alsoOnThisConnection' => AnonymousResourceCollection::class - ])] private static function createCheckin( Status $status, Trip $trip, @@ -132,7 +102,7 @@ private static function createCheckin( Carbon $departure, Carbon $arrival, bool $force = false, - ): array { + ): CheckinSuccessDto { $trip->load('stopovers'); //Note: Compare with ->format because of timezone differences! @@ -189,6 +159,7 @@ private static function createCheckin( forceCheckin: $force, ); try { + /** @var Checkin $checkin */ $checkin = Checkin::create([ 'status_id' => $status->id, 'user_id' => $status->user_id, @@ -208,11 +179,7 @@ private static function createCheckin( } } - return [ - 'status' => $status, - 'points' => $pointCalculation, - 'alsoOnThisConnection' => StatusResource::collection($alsoOnThisConnection) - ]; + return new CheckinSuccessDto($status, $pointCalculation, $alsoOnThisConnection); } catch (PDOException $exception) { if ($exception->getCode() === 23000) { // Integrity constraint violation: Duplicate entry throw new AlreadyCheckedInException(); @@ -264,10 +231,11 @@ public static function changeDestination( * @return Trip * @throws HafasException * @throws StationNotOnTripException + * @throws \JsonException * @api v1 */ public static function getHafasTrip(string $tripId, string $lineName, int $startId): Trip { - $hafasTrip = HafasController::getHafasTrip($tripId, $lineName); + $hafasTrip = (new CheckinHydratorRepository())->getHafasTrip($tripId, $lineName); $hafasTrip->loadMissing(['stopovers', 'originStation', 'destinationStation']); $originStopover = $hafasTrip->stopovers->filter(function(Stopover $stopover) use ($startId) { diff --git a/app/Http/Controllers/Backend/User/FollowController.php b/app/Http/Controllers/Backend/User/FollowController.php index 5d25a431b..fe1e5aea4 100644 --- a/app/Http/Controllers/Backend/User/FollowController.php +++ b/app/Http/Controllers/Backend/User/FollowController.php @@ -62,13 +62,17 @@ public static function rejectFollower(int $userId, int $followerID): ?FollowRequ * @param int $userId The id of the user who is approving a follower * @param int $approverId The id of a to-be-approved follower * - * @throws ModelNotFoundException|AlreadyFollowingException + * @throws ModelNotFoundException * @throws AuthorizationException */ public static function approveFollower(int $userId, int $approverId): bool { $request = FollowRequest::where('user_id', $approverId)->where('follow_id', $userId)->firstOrFail(); - $follow = UserController::createFollow($request->user, $request->requestedFollow, true); + try { + $follow = UserController::createFollow($request->user, $request->requestedFollow, true); + } catch (AlreadyFollowingException $e) { + $follow = true; + } if ($follow) { $request->delete(); diff --git a/app/Http/Controllers/Frontend/Admin/CheckinController.php b/app/Http/Controllers/Frontend/Admin/CheckinController.php index f9e3d74e9..1df1e3eea 100644 --- a/app/Http/Controllers/Frontend/Admin/CheckinController.php +++ b/app/Http/Controllers/Frontend/Admin/CheckinController.php @@ -12,6 +12,7 @@ use App\Http\Controllers\Backend\Transport\TrainCheckinController; use App\Http\Controllers\HafasController; use App\Http\Controllers\TransportController as TransportBackend; +use App\Hydrators\CheckinRequestHydrator; use App\Jobs\PostStatusOnMastodon; use App\Models\Event; use App\Models\Station; @@ -86,7 +87,7 @@ public function renderStationboard(Request $request): View|RedirectResponse { public function renderTrip(string $tripId, Request $request): RedirectResponse|View { $validated = $request->validate([ 'lineName' => ['required'], - 'startIBNR' => ['required', 'numeric'], + 'start' => ['required', 'numeric'], 'departure' => ['required', 'date'], 'userId' => ['nullable', 'numeric'] ]); @@ -100,7 +101,7 @@ public function renderTrip(string $tripId, Request $request): RedirectResponse|V $hafasTrip = TrainCheckinController::getHafasTrip( tripId: $tripId, lineName: $validated['lineName'], - startId: $validated['startIBNR'], + startId: $validated['start'], ); return view('admin.checkin.trip', [ 'hafasTrip' => $hafasTrip, @@ -122,14 +123,15 @@ public function checkin(Request $request): View|RedirectResponse { 'visibility' => ['nullable', new Enum(StatusVisibility::class)], 'eventId' => ['nullable', 'integer', 'exists:events,id'], 'toot' => ['nullable', 'max:2'], - 'shouldChain_check' => ['nullable', 'max:2'], + 'chainPost' => ['nullable', 'max:2'], 'tripId' => ['required'], 'lineName' => ['required'], - 'startIBNR' => ['required', 'numeric'], + 'start' => ['required', 'numeric'], 'destinationStopover' => ['required', 'exists:train_stopovers,id'], 'departure' => ['required', 'date'], 'force' => ['nullable', 'max:2'], - 'userId' => ['required', 'integer'] + 'userId' => ['required', 'integer'], + 'ibnr' => ['nullable', 'max:2'], ]); try { $user = User::findOrFail($validated['userId']); @@ -137,33 +139,12 @@ public function checkin(Request $request): View|RedirectResponse { return redirect()->back()->withErrors('User non-existent'); } - $destinationStopover = Stopover::findOrFail($validated['destinationStopover']); - try { - $backendResponse = TrainCheckinController::checkin( - user: $user, - trip: HafasController::getHafasTrip($validated['tripId'], $validated['lineName']), - origin: Station::where('ibnr', $validated['startIBNR'])->first(), - departure: Carbon::parse($validated['departure']), - destination: $destinationStopover->station, - arrival: $destinationStopover->arrival_planned, - travelReason: Business::tryFrom($validated['business'] ?? 0), - visibility: StatusVisibility::tryFrom($validated['visibility'] ?? 0), - body: $validated['body'] ?? null, - event: isset($validated['eventId']) ? Event::find($validated['eventId']) : null, - force: isset($validated['force']), - postOnMastodon: isset($request->toot_check), - shouldChain: isset($request->shouldChain_check) - ); - - $status = $backendResponse['status']; - - PostStatusOnMastodon::dispatchIf(isset($validated['toot']) - && $user?->socialProfile?->mastodon_id !== null, - $status); + $dto = (new CheckinRequestHydrator($validated, $user))->hydrateFromAdmin(); + $backendResponse = TrainCheckinController::checkin($dto); return redirect()->route('admin.stationboard') - ->with('alert-success', 'Checked in successfully. Earned points: ' . $backendResponse['points']->points); + ->with('alert-success', 'Checked in successfully. Earned points: ' . $backendResponse->pointCalculation->points); } catch (CheckInCollisionException $e) { return redirect() diff --git a/app/Http/Controllers/Frontend/Admin/ReportController.php b/app/Http/Controllers/Frontend/Admin/ReportController.php index 00ceed9aa..2c954547d 100644 --- a/app/Http/Controllers/Frontend/Admin/ReportController.php +++ b/app/Http/Controllers/Frontend/Admin/ReportController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers\Frontend\Admin; -use App\Enum\Report\ReportStatus; use App\Http\Controllers\Controller; use App\Models\Report; use Illuminate\View\View; @@ -13,7 +12,7 @@ class ReportController extends Controller public function renderReports(): View { $this->authorize('viewAny', Report::class); return view('admin.reports.list', [ - 'reports' => Report::paginate(15), + 'reports' => Report::orderByDesc('status')->orderByDesc('created_at')->paginate(15), ]); } diff --git a/app/Http/Controllers/Frontend/Admin/UserController.php b/app/Http/Controllers/Frontend/Admin/UserController.php index bb3cade6a..8ce87c821 100644 --- a/app/Http/Controllers/Frontend/Admin/UserController.php +++ b/app/Http/Controllers/Frontend/Admin/UserController.php @@ -8,6 +8,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; +use Spatie\Permission\Models\Role; class UserController { @@ -72,4 +73,26 @@ public function updateMail(Request $request): RedirectResponse { } return redirect()->route('admin.users.user', ['id' => $validated['id']]); } + + public function updateRoles(Request $request): RedirectResponse { + $validated = $request->validate([ + 'id' => ['required', 'integer', 'exists:users,id'], + 'roles' => ['array'], + ]); + $user = User::findOrFail($validated['id']); + $roles = []; + foreach (Role::all() as $role) { + if ($role->name === 'admin') { + continue; + } + if (isset($validated['roles'][$role->name])) { + $roles[] = $role->name; + } + } + if ($user->hasRole('admin')) { + $roles[] = 'admin'; + } + $user->syncRoles($roles); + return redirect()->route('admin.users.user', ['id' => $validated['id']]); + } } diff --git a/app/Http/Controllers/Frontend/LeaderboardController.php b/app/Http/Controllers/Frontend/LeaderboardController.php index 883d2813c..0f7002af8 100644 --- a/app/Http/Controllers/Frontend/LeaderboardController.php +++ b/app/Http/Controllers/Frontend/LeaderboardController.php @@ -7,6 +7,7 @@ use App\Http\Controllers\Controller; use Carbon\Carbon; use Illuminate\Contracts\Support\Renderable; +use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Gate; use stdClass; @@ -15,7 +16,11 @@ class LeaderboardController extends Controller { private static string $cacheRetentionConfigKey = 'trwl.cache.leaderboard-retention-seconds'; - public static function renderMonthlyLeaderboard(string $date): Renderable { + public static function renderMonthlyLeaderboard(string $date): Renderable|RedirectResponse { + if (auth()->user()?->points_enabled === false) { + return redirect()->route('dashboard'); + } + $date = Carbon::parse($date); $leaderboard = Cache::remember( @@ -32,7 +37,11 @@ public static function renderMonthlyLeaderboard(string $date): Renderable { ]); } - public function renderLeaderboard(): Renderable { + public function renderLeaderboard(): Renderable|RedirectResponse { + if (auth()->user()?->points_enabled === false) { + return redirect()->route('dashboard'); + } + $ttl = config(self::$cacheRetentionConfigKey); $usersLeaderboard = Cache::remember( diff --git a/app/Http/Controllers/Frontend/SettingsController.php b/app/Http/Controllers/Frontend/SettingsController.php index 18d00806a..35eebf93f 100644 --- a/app/Http/Controllers/Frontend/SettingsController.php +++ b/app/Http/Controllers/Frontend/SettingsController.php @@ -89,6 +89,7 @@ public function updateMainSettings(Request $request): RedirectResponse { public function updatePrivacySettings(Request $request): RedirectResponse { $validated = $request->validate([ 'likes_enabled' => ['nullable'], + 'points_enabled' => ['nullable'], 'private_profile' => ['nullable'], 'prevent_index' => ['required', 'gte:0', 'lte:1'], 'privacy_hide_days' => ['nullable', 'gte:1',], @@ -107,6 +108,8 @@ public function updatePrivacySettings(Request $request): RedirectResponse { $user->update([ 'likes_enabled' => isset($validated['likes_enabled']) && $validated['likes_enabled'] === 'on', + 'points_enabled' => isset($validated['points_enabled']) + && $validated['points_enabled'] === 'on', 'prevent_index' => $validated['prevent_index'], 'private_profile' => isset($validated['private_profile']) && $validated['private_profile'] === 'on', diff --git a/app/Http/Controllers/Frontend/Support/SupportController.php b/app/Http/Controllers/Frontend/Support/SupportController.php deleted file mode 100644 index de47c0d41..000000000 --- a/app/Http/Controllers/Frontend/Support/SupportController.php +++ /dev/null @@ -1,42 +0,0 @@ - isset(auth()->user()->email, auth()->user()->email_verified_at) - ]); - } - - public function submit(Request $request): RedirectResponse { - $validated = $request->validate([ - 'subject' => ['required', 'string', 'max:255'], - 'message' => ['required', 'string',] - ]); - - try { - $ticketNumber = TicketController::createTicket( - user: auth()->user(), - subject: $validated['subject'], - message: $validated['message'], - ); - return back()->with('success', __('support.success', ['ticketNumber' => $ticketNumber])); - } catch (GuzzleException $exception) { - report($exception); - return back()->with('error', __('messages.exception.general')); - } catch (RateLimitExceededException) { - return back()->with('error', __('support.rate_limit_exceeded')); - } - } -} diff --git a/app/Http/Controllers/FrontendStatusController.php b/app/Http/Controllers/FrontendStatusController.php index 284c10218..5faa5b45b 100644 --- a/app/Http/Controllers/FrontendStatusController.php +++ b/app/Http/Controllers/FrontendStatusController.php @@ -24,25 +24,11 @@ class FrontendStatusController extends Controller public function getDashboard(): Renderable|RedirectResponse { $statuses = DashboardController::getPrivateDashboard(auth()->user()); - if ($statuses->isEmpty() || auth()->user()->follows->count() === 0) { - if (Session::has('checkin-success')) { - return redirect()->route('globaldashboard') - ->with('checkin-success', Session::get('checkin-success')); - } - if (Session::has('error')) { - return redirect()->route('globaldashboard') - ->with('error', Session::get('error')); - } - if (Session::has('checkin-collision')) { - return redirect()->route('globaldashboard') - ->with('checkin-collision', Session::get('checkin-collision')); - } - return redirect()->route('globaldashboard'); - } return view('dashboard', [ 'statuses' => $statuses, 'latest' => StationController::getLatestArrivals(auth()->user()), - 'future' => StatusBackend::getFutureCheckins() + 'future' => StatusBackend::getFutureCheckins(), + 'showGlobalButton' => auth()->user()->follows->count() === 0 ]); } @@ -50,7 +36,8 @@ public function getGlobalDashboard(): Renderable { return view('dashboard', [ 'statuses' => DashboardController::getGlobalDashboard(Auth::user()), 'latest' => StationController::getLatestArrivals(Auth::user()), - 'future' => StatusBackend::getFutureCheckins() + 'future' => StatusBackend::getFutureCheckins(), + 'showGlobalButton' => false ]); } diff --git a/app/Http/Controllers/HafasController.php b/app/Http/Controllers/HafasController.php index b6e3a0221..92c190be3 100644 --- a/app/Http/Controllers/HafasController.php +++ b/app/Http/Controllers/HafasController.php @@ -341,21 +341,6 @@ private static function fetchStation(int $ibnr): Station { } - /** - * @param string $tripID - * @param string $lineName - * - * @return Trip - * @throws HafasException - */ - public static function getHafasTrip(string $tripID, string $lineName): Trip { - if (is_numeric($tripID)) { - $trip = Trip::where('id', $tripID)->where('linename', $lineName)->first(); - } - $trip = $trip ?? Trip::where('trip_id', $tripID)->where('linename', $lineName)->first(); - return $trip ?? self::fetchHafasTrip($tripID, $lineName); - } - /** * @throws HafasException|JsonException */ @@ -386,7 +371,7 @@ public static function fetchRawHafasTrip(string $tripId, string $lineName) { * @param string $lineName * * @return Trip - * @throws HafasException + * @throws HafasException|JsonException */ public static function fetchHafasTrip(string $tripID, string $lineName): Trip { $tripJson = self::fetchRawHafasTrip($tripID, $lineName); diff --git a/app/Http/Controllers/SitemapController.php b/app/Http/Controllers/SitemapController.php index 9b193fe1b..46fcbb08c 100644 --- a/app/Http/Controllers/SitemapController.php +++ b/app/Http/Controllers/SitemapController.php @@ -40,7 +40,6 @@ public function renderSitemap(Request $request): Response { } private function addStatic(Sitemap $sitemap): void { - $sitemap->add(Url::create(route('static.about'))->setPriority(0.7)); $sitemap->add(Url::create(route('leaderboard'))->setPriority(0.7)); $sitemap->add(Url::create(route('statuses.active'))->setPriority(0.6)); diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 4158f3cb7..e516ac3fd 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -15,6 +15,7 @@ use App\Notifications\StatusLiked; use Carbon\Carbon; use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -269,11 +270,11 @@ public static function getFutureCheckins(): Paginator { } public static function createStatus( - User $user, - Business $business, - StatusVisibility $visibility, - string $body = null, - Event $event = null + User|Authenticatable $user, + Business $business, + StatusVisibility $visibility, + string $body = null, + Event $event = null ): Status { if ($event !== null && !Carbon::now()->isBetween($event->checkin_start, $event->checkin_end)) { Log::info('Event checkin was prevented because the event is not active anymore', [ diff --git a/app/Http/Resources/CheckinSuccessResource.php b/app/Http/Resources/CheckinSuccessResource.php new file mode 100644 index 000000000..64db63fed --- /dev/null +++ b/app/Http/Resources/CheckinSuccessResource.php @@ -0,0 +1,38 @@ + new StatusResource($this->status), + //ToDo: Rewrite ['points'] so the DTO will match the documented structure -> non-breaking api change + 'points' => [ + 'points' => $this->pointCalculation->points, + 'calculation' => [ + 'base' => $this->pointCalculation->basePoints, + 'distance' => $this->pointCalculation->distancePoints, + 'factor' => $this->pointCalculation->factor, + 'reason' => $this->pointCalculation->reason->value, + ], + 'additional' => null, //unused old attribute (not removed so this isn't breaking) + ], + 'alsoOnThisConnection' => StatusResource::collection($this->alsoOnThisConnection) + ]; + } +} diff --git a/app/Http/Resources/StatusResource.php b/app/Http/Resources/StatusResource.php index 94fe63d95..cc800e31c 100644 --- a/app/Http/Resources/StatusResource.php +++ b/app/Http/Resources/StatusResource.php @@ -4,6 +4,7 @@ use App\Dto\MentionDto; use App\Http\Controllers\Backend\User\ProfilePictureController; +use App\Models\Status; use App\Models\StatusTag; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Facades\Gate; @@ -21,7 +22,7 @@ * @OA\Property(property="isLikable", description="Do the author of this status and the currently authenticated user allow liking of statuses? Only show the like UI if set to true",type="boolean", example=true), * @OA\Property(property="client", ref="#/components/schemas/ClientResource"), * @OA\Property(property="createdAt", description="creation date of this status",type="string",format="datetime", example="2022-07-17T13:37:00+02:00"), - * @OA\Property(property="train", description="Train model"), + * @OA\Property(property="train", ref="#/components/schemas/TransportResource"), * @OA\Property(property="event", ref="#/components/schemas/EventResource", nullable=true), * @OA\Property(property="userDetails", ref="#/components/schemas/LightUserResource"), * @OA\Property(property="tags", type="array", @OA\Items(ref="#/components/schemas/StatusTagResource")), @@ -30,6 +31,7 @@ class StatusResource extends JsonResource { public function toArray($request): array { + /** @var Status $this */ return [ 'id' => (int) $this->id, 'body' => (string) $this->body, @@ -47,22 +49,7 @@ public function toArray($request): array { 'isLikable' => Gate::allows('like', $this->resource), 'client' => new ClientResource($this->client), 'createdAt' => $this->created_at->toIso8601String(), - 'train' => [ //TODO: don't call it train - we have more than trains - 'trip' => (int) $this->checkin->trip->id, - 'hafasId' => (string) $this->checkin->trip->trip_id, - 'category' => (string) $this->checkin->trip->category->value, - 'number' => (string) $this->checkin->trip->number, - 'lineName' => (string) $this->checkin->trip->linename, - 'journeyNumber' => $this->checkin->trip->journey_number, - 'distance' => (int) $this->checkin->distance, - 'points' => (int) $this->checkin->points, - 'duration' => (int) $this->checkin->duration, - 'manualDeparture' => $this->checkin->manual_departure?->toIso8601String(), - 'manualArrival' => $this->checkin->manual_arrival?->toIso8601String(), - 'origin' => new StopoverResource($this->checkin->originStopover), - 'destination' => new StopoverResource($this->checkin->destinationStopover), - 'operator' => new OperatorResource($this?->checkin->trip->operator) - ], + 'train' => new TransportResource($this->checkin), //TODO: don't call it train - we have more than trains 'event' => new EventResource($this?->event), 'userDetails' => new LightUserResource($this->user), //TODO: rename this to user, after deprecated fields are removed (2024-08) 'tags' => StatusTagResource::collection($this->tags->filter(fn(StatusTag $tag) => Gate::allows('view', $tag))), diff --git a/app/Http/Resources/StopoverResource.php b/app/Http/Resources/StopoverResource.php index b558d13ab..643623006 100644 --- a/app/Http/Resources/StopoverResource.php +++ b/app/Http/Resources/StopoverResource.php @@ -2,17 +2,43 @@ namespace App\Http\Resources; +use App\Models\Stopover; use Illuminate\Http\Resources\Json\JsonResource; +use OpenApi\Annotations as OA; +/** + * @OA\Schema( + * title="StopoverResource", + * @OA\Property(property="id", type="integer", example=12345), + * @OA\Property(property="name", type="string", example="Karlsruhe Hbf", description="name of the station"), + * @OA\Property(property="rilIdentifier", type="string", example="RK", nullable=true, description="Identifier specified in 'Richtline 100' of the Deutsche Bahn"), + * @OA\Property(property="evaIdentifier", type="string", example="8000191", nullable=true, description="IBNR identifier of Deutsche Bahn"), + * @OA\Property(property="arrival", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="currently known arrival time. Equal to arrivalReal if known. Else equal to arrivalPlanned."), + * @OA\Property(property="arrivalPlanned", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="planned arrival according to timetable records"), + * @OA\Property(property="arrivalReal", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="real arrival according to live data"), + * @OA\Property(property="arrivalPlatformPlanned", type="string", example="5", nullable=true, description="planned arrival platform according to timetable records"), + * @OA\Property(property="arrivalPlatformReal", type="string", example="5 A-F", nullable=true, description="real arrival platform according to live data"), + * @OA\Property(property="departure", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="currently known departure time. Equal to departureReal if known. Else equal to departurePlanned."), + * @OA\Property(property="departurePlanned", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="planned departure according to timetable records"), + * @OA\Property(property="departureReal", type="string", format="date-time", example="2022-07-17T13:37:00+02:00", nullable=true, description="real departure according to live data"), + * @OA\Property(property="departurePlatformPlanned", type="string", example="5", nullable=true, description="planned departure platform according to timetable records"), + * @OA\Property(property="departurePlatformReal", type="string", example="5 A-F", nullable=true, description="real departure platform according to live data"), + * @OA\Property(property="platform", type="string", example="5 A-F", nullable=true), + * @OA\Property(property="isArrivalDelayed", type="boolean", example=false, description="Is there a delay in the arrival time?"), + * @OA\Property(property="isDepartureDelayed", type="boolean", example=false, description="Is there a delay in the departure time?"), + * @OA\Property(property="cancelled", type="boolean", example=false, description="is this stopover cancelled?"), + * ) + */ class StopoverResource extends JsonResource { public function toArray($request): array { + /** @var Stopover $this */ return [ 'id' => (int) $this->train_station_id, 'name' => $this->station->name, 'rilIdentifier' => $this->station->rilIdentifier ?? null, - 'evaIdentifier' => $this->station->ibnr, + 'evaIdentifier' => $this->station->ibnr ?? null, 'arrival' => $this->arrival?->toIso8601String(), //TODO: not necessary if planned and real are available 'arrivalPlanned' => $this->arrival_planned?->toIso8601String(), 'arrivalReal' => $this->arrival_real?->toIso8601String(), diff --git a/app/Http/Resources/TransportResource.php b/app/Http/Resources/TransportResource.php new file mode 100644 index 000000000..61ecc19d5 --- /dev/null +++ b/app/Http/Resources/TransportResource.php @@ -0,0 +1,50 @@ +user()?->points_enabled ?? true; + /** @var Checkin $this */ + return [ + 'trip' => (int) $this->trip->id, + 'hafasId' => (string) $this->trip->trip_id, + 'category' => (string) $this->trip->category->value, + 'number' => (string) $this->trip->number, + 'lineName' => (string) $this->trip->linename, + 'journeyNumber' => $this->trip->journey_number, + 'distance' => (int) $this->distance, + 'points' => (int) $pointsEnabled ? $this->points : 0, + 'duration' => (int) $this->duration, + 'manualDeparture' => $this->manual_departure?->toIso8601String(), + 'manualArrival' => $this->manual_arrival?->toIso8601String(), + 'origin' => new StopoverResource($this->originStopover), + 'destination' => new StopoverResource($this->destinationStopover), + 'operator' => new OperatorResource($this?->trip->operator) + ]; + } +} diff --git a/app/Http/Resources/UserAuthResource.php b/app/Http/Resources/UserAuthResource.php index 39cf930a1..b2ab1df21 100644 --- a/app/Http/Resources/UserAuthResource.php +++ b/app/Http/Resources/UserAuthResource.php @@ -28,6 +28,7 @@ class UserAuthResource extends JsonResource { public function toArray($request): array { + $pointsEnabled = $request->user()?->points_enabled ?? true; return [ 'id' => (int) $this->id, 'displayName' => (string) $this->name, @@ -37,7 +38,7 @@ public function toArray($request): array { 'totalDistance' => (float) $this->train_distance, 'trainDuration' => (int) $this->train_duration, // @deprecated: remove after 2024-08 'totalDuration' => (int) $this->train_duration, - 'points' => (int) $this->points, + 'points' => (int) $pointsEnabled ? $this->points : 0, 'mastodonUrl' => $this->mastodonUrl ?? null, 'privateProfile' => (bool) $this->private_profile, 'preventIndex' => $this->prevent_index, diff --git a/app/Http/Resources/UserBaseResource.php b/app/Http/Resources/UserBaseResource.php index 2a45997b2..ce1490fee 100644 --- a/app/Http/Resources/UserBaseResource.php +++ b/app/Http/Resources/UserBaseResource.php @@ -9,6 +9,7 @@ class UserBaseResource extends JsonResource { public function toArray($request): array { + $pointsEnabled = $request->user()?->points_enabled ?? true; return [ 'id' => (int) $this->id, 'displayName' => (string) $this->name, @@ -18,11 +19,12 @@ public function toArray($request): array { 'totalDistance' => (float) $this->train_distance, 'trainDuration' => (int) $this->train_duration, // @deprecated: remove after 2024-08 'totalDuration' => (int) $this->train_duration, - 'points' => (int) $this->points, + 'points' => (int) $pointsEnabled ? $this->points : 0, 'mastodonUrl' => $this->mastodonUrl ?? null, 'privateProfile' => (bool) $this->private_profile, 'preventIndex' => $this->prevent_index, 'likes_enabled' => $this->likes_enabled, + 'pointsEnabled' => $this->points_enabled, $this->mergeWhen(isset($this->UserSettingsResource), [ 'home' => $this->home, diff --git a/app/Hydrators/CheckinRequestHydrator.php b/app/Hydrators/CheckinRequestHydrator.php new file mode 100644 index 000000000..101a74cb7 --- /dev/null +++ b/app/Hydrators/CheckinRequestHydrator.php @@ -0,0 +1,108 @@ +validated = $validated; + $this->dto = $dto ?? new CheckInRequestDto(); + $this->user = $user ?? Auth::user(); + $this->repository = $repository ?? new CheckinHydratorRepository(); + } + + /** + * @throws HafasException + */ + public function hydrateFromApi(): CheckInRequestDto { + $this->parseApiFields(); + + return $this->dto; + } + + /** + * @throws HafasException + * @throws JsonException + */ + public function hydrateFromAdmin(): CheckInRequestDto { + $this->parseAdminFields(); + + return $this->dto; + } + + /** + * @throws HafasException + * @throws JsonException + */ + private function parseAdminFields(): void { + $this->parseDefaultFields(); + $destinationStopover = $this->repository->findOrFailStopover($this->validated['destinationStopover']); + + $this->dto->setDestination($destinationStopover->station) + ->setArrival($destinationStopover->arrival_planned); + } + + /** + * @throws HafasException + * @throws JsonException + */ + private function parseApiFields(): void { + $this->parseDefaultFields(); + + $arrival = Carbon::parse($this->validated['arrival']); + $destinationStation = $this->repository->getOneStation($this->searchKey, $this->validated['destination']); + + $this->dto->setArrival($arrival) + ->setDestination($destinationStation); + } + + /** + * @throws HafasException + * @throws JsonException + */ + private function parseDefaultFields(): void { + $this->searchKey = empty($this->validated['ibnr']) ? 'id' : 'ibnr'; + $originStation = $this->repository->getOneStation($this->searchKey, $this->validated['start']); + $departure = Carbon::parse($this->validated['departure']); + $travelReason = Business::tryFrom($this->validated['business'] ?? Business::PRIVATE->value); + $visibility = StatusVisibility::tryFrom($this->validated['visibility'] ?? StatusVisibility::PUBLIC->value); + $event = isset($this->validated['eventId']) ? $this->repository->findEvent($this->validated['eventId']) : null; + $trip = $this->repository->getHafasTrip($this->validated['tripId'], $this->validated['lineName']); + + $this->dto->setUser($this->user) + ->setTrip($trip) + ->setOrigin($originStation) + ->setDeparture($departure) + ->setTravelReason($travelReason) + ->setStatusVisibility($visibility) + ->setBody($this->validated['body'] ?? null) + ->setEvent($event) + ->setForceFlag(!empty($this->validated['force'])) + ->setPostOnMastodonFlag(!empty($this->validated['toot'])) + ->setChainFlag(!empty($this->validated['chainPost'])); + } +} diff --git a/app/Models/Checkin.php b/app/Models/Checkin.php index aa319adbc..36fabe9fd 100644 --- a/app/Models/Checkin.php +++ b/app/Models/Checkin.php @@ -16,34 +16,35 @@ /** * //properties - * @property int $id - * @property int $status_id - * @property int $user_id - * @property string $trip_id - * @property int $origin_stopover_id - * @property int $destination_stopover_id - * @property int $distance - * @property int $duration - * @property UTCDateTime $departure @deprecated -> use origin_stopover instead - * @property UTCDateTime $manual_departure - * @property UTCDateTime $arrival @deprecated -> use destination_stopover instead - * @property UTCDateTime $manual_arrival - * @property int $points - * @property bool $forced + * @property int $id + * @property int $status_id + * @property int $user_id + * @property string $trip_id + * @property int $origin_stopover_id + * @property int $destination_stopover_id + * @property int $distance + * @property int $duration + * @property UTCDateTime $departure @deprecated -> use origin_stopover instead + * @property UTCDateTime $manual_departure + * @property UTCDateTime $arrival @deprecated -> use destination_stopover instead + * @property UTCDateTime $manual_arrival + * @property int $points + * @property bool $forced * * //relations - * @property Trip $trip - * @property Status $status - * @property User $user - * @property Station $originStation - * @property Stopover $originStopover - * @property Station $destinationStation - * @property Stopover $destinationStopover + * @property Trip $trip + * @property Status $status + * @property User $user + * @property Station $originStation + * @property Stopover $originStopover + * @property Station $destinationStation + * @property Stopover $destinationStopover * * //appends - * @property float $speed - * @property stdClass $displayDeparture - * @property stdClass $displayArrival + * @property float $speed + * @property stdClass $displayDeparture + * @property stdClass $displayArrival + * @property Collection $alsoOnThisConnection * * @todo rename table to "Checkin" (without Train - we have more than just trains) * @todo merge model with "Status" because the difference between trip sources (HAFAS, @@ -89,14 +90,6 @@ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id', 'id'); } - public function originStation(): HasOne { - return $this->hasOne(Station::class, 'ibnr', 'origin'); - } - - public function destinationStation(): HasOne { - return $this->hasOne(Station::class, 'ibnr', 'destination'); - } - public function trip(): HasOne { return $this->hasOne(Trip::class, 'trip_id', 'trip_id'); } @@ -169,7 +162,7 @@ public function getSpeedAttribute(): float { } /** - * @return Collection + * @return Collection * @todo Sichtbarkeit der CheckIns prüfen! Hier werden auch Private CheckIns angezeigt */ public function getAlsoOnThisConnectionAttribute(): Collection { diff --git a/app/Models/Report.php b/app/Models/Report.php index 0bb91c105..4c4ea3e37 100644 --- a/app/Models/Report.php +++ b/app/Models/Report.php @@ -6,10 +6,18 @@ use App\Enum\Report\ReportStatus; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphMany; use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; +/** + * @property int $id @todo change to uuid (currently not used so easy without break something) + * @property ReportStatus $status + * @property string $subject_type + * @property int $subject_id @todo make it a string to support uuid + * @property ReportReason $reason + * @property string $description + * @property int $reporter_id + */ class Report extends Model { use LogsActivity; diff --git a/app/Models/Station.php b/app/Models/Station.php index a82a35897..e5835f467 100644 --- a/app/Models/Station.php +++ b/app/Models/Station.php @@ -10,17 +10,17 @@ use Spatie\Activitylog\Traits\LogsActivity; /** - * @property int $id - * @property int $ibnr - * @property string $rilIdentifier - * @property string $name - * @property double $latitude - * @property double $longitude - * @property int $time_offset - * @property bool $shift_time - * @property Event[] $events - * @property Carbon $created_at - * @property Carbon $updated_at + * @property int $id + * @property int|null $ibnr + * @property string $rilIdentifier + * @property string $name + * @property double $latitude + * @property double $longitude + * @property int $time_offset + * @property bool $shift_time + * @property Event[] $events + * @property Carbon $created_at + * @property Carbon $updated_at * @todo rename table to "Station" (without Train - we have more than just trains) */ class Station extends Model diff --git a/app/Models/Stopover.php b/app/Models/Stopover.php index 4c9dca259..be8261564 100644 --- a/app/Models/Stopover.php +++ b/app/Models/Stopover.php @@ -21,6 +21,8 @@ * @property UTCDateTime $departure_real * @property string $departure_platform_planned * @property string $departure_platform_real + * @property bool $isArrivalDelayed + * @property bool $isDepartureDelayed * @property bool $cancelled * * relations @@ -59,7 +61,10 @@ class Stopover extends Model 'departure_planned' => UTCDateTime::class, 'departure_real' => UTCDateTime::class, 'departure_platform_planned' => 'string', - 'departure_platform_real' => 'string' + 'departure_platform_real' => 'string', + 'isArrivalDelayed' => 'boolean', + 'isDepartureDelayed' => 'boolean', + 'cancelled' => 'boolean', ]; public function trip(): BelongsTo { diff --git a/app/Models/User.php b/app/Models/User.php index 1f817c534..467902d68 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -41,12 +41,25 @@ * @property boolean private_profile * @property boolean prevent_index * @property boolean likes_enabled + * @property boolean points_enabled * @property MapProvider mapprovider * @property string timezone * @property FriendCheckinSetting friend_checkin * @property int privacy_hide_days * @property string language * @property Carbon last_login + * @property Status[] $statuses + * @property SocialLoginProfile socialProfile + * @property int points + * @property boolean userInvisibleToMe + * @property string mastodonUrl + * @property int train_distance + * @property int train_duration + * @property boolean following + * @property boolean followPending + * @property boolean muted + * @property boolean isAuthUserBlocked + * @property boolean isBlockedByAuthUser * * // relationships * @property Collection trainCheckins @@ -67,11 +80,9 @@ * @property Collection statuses * @property Collection trustedUsers * - * @todo replace "role" with an explicit permission system - e.g. spatie/laravel-permission - * @todo replace "experimental" also with an explicit permission system - user can add self to "experimental" group + * * @todo rename home_id to home_station_id * @todo rename mapprovider to map_provider - * @todo remove "twitterUrl" (Twitter isn't used by traewelling anymore) * @mixin Builder */ class User extends Authenticatable implements MustVerifyEmail @@ -81,8 +92,8 @@ class User extends Authenticatable implements MustVerifyEmail protected $fillable = [ 'username', 'name', 'avatar', 'email', 'email_verified_at', 'password', 'home_id', 'privacy_ack_at', - 'default_status_visibility', 'likes_enabled', 'private_profile', 'prevent_index', 'privacy_hide_days', - 'language', 'last_login', 'mapprovider', 'timezone', 'friend_checkin', + 'default_status_visibility', 'likes_enabled', 'points_enabled', 'private_profile', 'prevent_index', + 'privacy_hide_days', 'language', 'last_login', 'mapprovider', 'timezone', 'friend_checkin', ]; protected $hidden = [ 'password', 'remember_token', 'email', 'email_verified_at', 'privacy_ack_at', @@ -99,6 +110,7 @@ class User extends Authenticatable implements MustVerifyEmail 'home_id' => 'integer', 'private_profile' => 'boolean', 'likes_enabled' => 'boolean', + 'points_enabled' => 'boolean', 'default_status_visibility' => StatusVisibility::class, 'prevent_index' => 'boolean', 'privacy_hide_days' => 'integer', diff --git a/app/Observers/ReportObserver.php b/app/Observers/ReportObserver.php new file mode 100644 index 000000000..42a1ee43c --- /dev/null +++ b/app/Observers/ReportObserver.php @@ -0,0 +1,27 @@ + config('app.admin.notification.chat_id'), + 'text' => "🚨 New Report for " . $report->subject_type . "" . PHP_EOL + . "Reason: " . $report->reason?->value . PHP_EOL + . "Description: " . ($report->description ?? 'None') . PHP_EOL + . "View Report: " . config('app.url') . "/admin/reports/" . $report->id . PHP_EOL + , + 'parse_mode' => 'HTML', + ]); + } +} diff --git a/app/Observers/TripObserver.php b/app/Observers/TripObserver.php new file mode 100644 index 000000000..a41161b1f --- /dev/null +++ b/app/Observers/TripObserver.php @@ -0,0 +1,22 @@ +checkAndReport( + $trip->linename, + ReportableSubject::TRIP, + $trip->id + ); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 4cd50c679..e23022e87 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -14,12 +14,16 @@ use App\Models\Checkin; use App\Models\Follow; use App\Models\Like; +use App\Models\Report; use App\Models\Status; +use App\Models\Trip; use App\Models\User; use App\Observers\CheckinObserver; use App\Observers\FollowObserver; use App\Observers\LikeObserver; +use App\Observers\ReportObserver; use App\Observers\StatusObserver; +use App\Observers\TripObserver; use App\Observers\UserObserver; use Illuminate\Auth\Events\Registered; use Illuminate\Cache\Events\CacheMissed; @@ -59,10 +63,12 @@ class EventServiceProvider extends ServiceProvider ]; protected $observers = [ + Checkin::class => [CheckinObserver::class], Follow::class => [FollowObserver::class], Like::class => [LikeObserver::class], + Report::class => [ReportObserver::class], Status::class => [StatusObserver::class], - Checkin::class => [CheckinObserver::class], + Trip::class => [TripObserver::class], User::class => [UserObserver::class], ]; diff --git a/app/Repositories/CheckinHydratorRepository.php b/app/Repositories/CheckinHydratorRepository.php new file mode 100644 index 000000000..ed1e8cde7 --- /dev/null +++ b/app/Repositories/CheckinHydratorRepository.php @@ -0,0 +1,38 @@ +first(); + } + + /** + * @throws HafasException + * @throws JsonException + */ + public function getHafasTrip(string $tripID, string $lineName): Trip { + if (is_numeric($tripID)) { + $trip = Trip::where('id', $tripID)->where('linename', $lineName)->first(); + } + $trip = $trip ?? Trip::where('trip_id', $tripID)->where('linename', $lineName)->first(); + return $trip ?? HafasController::fetchHafasTrip($tripID, $lineName); + } + + public function findEvent(int $id): ?Event { + return Event::find($id); + } +} diff --git a/app/Repositories/ReportRepository.php b/app/Repositories/ReportRepository.php new file mode 100644 index 000000000..7d7b75883 --- /dev/null +++ b/app/Repositories/ReportRepository.php @@ -0,0 +1,29 @@ + 'App\\Models\\' . $subjectType->value, + 'subject_id' => $subjectId, + 'reason' => $reason->value, + 'description' => $description ?? null, + 'reporter_id' => $reporter?->id, + ]); + } +} diff --git a/app/Services/ReportService.php b/app/Services/ReportService.php new file mode 100644 index 000000000..e6f7eb643 --- /dev/null +++ b/app/Services/ReportService.php @@ -0,0 +1,51 @@ +reportRepository = $reportRepository ?? new ReportRepository(); + $this->triggerWords = $triggerWords ?? ['auto', 'fuss', 'fuß', 'fahrrad', 'foot', 'car', 'bike']; + } + + public function checkString(string $haystack): array { + $matches = []; + + foreach ($this->triggerWords as $triggerWord) { + if (str_contains(strtolower($haystack), $triggerWord)) { + $matches[] = $triggerWord; + } + } + + return $matches; + } + + public function checkAndReport(string $haystack, ReportableSubject $subjectType, int $subjectId): void { + $matches = $this->checkString($haystack); + + if ($matches === []) { + return; + } + + $info = sprintf('Automatically reported: The %s is inappropriate because it contains the words "%s".', $subjectType->value, implode('", "', $matches)); + Log::info($info); + + $this->reportRepository->createReport( + $subjectType, + $subjectId, + ReportReason::INAPPROPRIATE, + $info, + ); + } +} diff --git a/app/Virtual/Models/CheckinResponse.php b/app/Virtual/Models/CheckinResponse.php deleted file mode 100644 index 3b619a739..000000000 --- a/app/Virtual/Models/CheckinResponse.php +++ /dev/null @@ -1,50 +0,0 @@ -=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.1", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -11064,7 +11063,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.28" }, "funding": [ { @@ -11080,7 +11079,7 @@ "type": "tidelift" } ], - "time": "2024-06-20T13:09:54+00:00" + "time": "2024-07-18T14:54:16+00:00" }, { "name": "roave/security-advisories", @@ -11088,12 +11087,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "27714b56f04815b654c3805502ab77207505ac19" + "reference": "4ee5bb22f4c3a7ced8de2ff3e7abce703d546de8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/27714b56f04815b654c3805502ab77207505ac19", - "reference": "27714b56f04815b654c3805502ab77207505ac19", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4ee5bb22f4c3a7ced8de2ff3e7abce703d546de8", + "reference": "4ee5bb22f4c3a7ced8de2ff3e7abce703d546de8", "shasum": "" }, "conflict": { @@ -11101,7 +11100,10 @@ "admidio/admidio": "<4.2.13", "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.04.6", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9", "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", "airesvsg/acf-to-rest-api": "<=3.1", @@ -11127,12 +11129,13 @@ "athlon1600/php-proxy": "<=5.1", "athlon1600/php-proxy-app": "<=3", "austintoddj/canvas": "<=3.4.2", - "automad/automad": "<=1.10.9", + "auth0/wordpress": "<=4.6", + "automad/automad": "<=2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<3.288.1", "azuracast/azuracast": "<0.18.3", - "backdrop/backdrop": "<1.24.2", + "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", "backpack/crud": "<3.4.9", "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", "badaso/core": "<2.7", @@ -11197,7 +11200,7 @@ "contao/managed-edition": "<=1.5", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", - "craftcms/cms": "<4.6.2", + "craftcms/cms": "<4.6.2|>=5.0.0.0-beta1,<=5.2.2", "croogo/croogo": "<4", "cuyz/valinor": "<0.12", "czproject/git-php": "<4.0.3", @@ -11232,7 +11235,7 @@ "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", - "egroupware/egroupware": "<16.1.20170922", + "egroupware/egroupware": "<23.1.20240624", "elefant/cms": "<2.0.7", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "elijaa/phpmemcacheadmin": "<=1.3", @@ -11295,7 +11298,7 @@ "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1,<1.3.5", "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", - "friendsofsymfony1/symfony1": ">=1.1,<1.15.19", + "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", "froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3", @@ -11418,7 +11421,7 @@ "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", - "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2.0-patch2", + "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", "magneto/core": "<1.9.4.4-dev", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", @@ -11489,7 +11492,7 @@ "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", "open-web-analytics/open-web-analytics": "<1.7.4", - "opencart/opencart": "<=3.0.3.9|>=4", + "opencart/opencart": ">=0", "openid/php-openid": "<2.3", "openmage/magento-lts": "<20.5", "opensolutions/vimbadmin": "<=3.0.15", @@ -11565,8 +11568,8 @@ "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4", - "processwire/processwire": "<=3.0.210", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", + "processwire/processwire": "<=3.0.229", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", "pterodactyl/panel": "<1.11.6", @@ -11605,9 +11608,9 @@ "shopware/core": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", "shopware/platform": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<6.2.3", + "shopware/shopware": "<=5.7.17", "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", - "shopxo/shopxo": "<2.2.6", + "shopxo/shopxo": "<=6.1", "showdoc/showdoc": "<2.10.4", "silverstripe-australia/advancedreports": ">=1,<=2", "silverstripe/admin": "<1.13.19|>=2,<2.1.8", @@ -11615,11 +11618,12 @@ "silverstripe/cms": "<4.11.3", "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<4.13.39|>=5,<5.1.11", + "silverstripe/framework": "<5.2.16", "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", "silverstripe/recipe-cms": ">=4.5,<4.5.3", "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/reports": "<5.2.3", "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", "silverstripe/subsites": ">=2,<2.6.1", @@ -11649,7 +11653,7 @@ "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<22.02.3", + "ssddanbrown/bookstack": "<24.05.1", "statamic/cms": "<4.46|>=5.3,<5.6.2", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<2.1.62", @@ -11668,7 +11672,7 @@ "sylius/grid-bundle": "<1.10.1", "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2|>=1.12.0.0-alpha1,<1.12.16|>=1.13.0.0-alpha1,<1.13.1", + "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", "symbiote/silverstripe-multivaluefield": ">=3,<3.1", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-seed": "<6.0.3", @@ -11727,7 +11731,7 @@ "topthink/framework": "<6.0.17|>=6.1,<6.1.5|>=8,<8.0.4", "topthink/think": "<=6.1.1", "topthink/thinkphp": "<=3.2.3", - "torrentpier/torrentpier": "<=2.4.1", + "torrentpier/torrentpier": "<=2.4.3", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", "tribalsystems/zenario": "<9.5.60602", "truckersmp/phpwhois": "<=4.3.1", @@ -11769,7 +11773,8 @@ "wallabag/tcpdf": "<6.2.22", "wallabag/wallabag": "<2.6.7", "wanglelecc/laracms": "<=1.0.3", - "web-auth/webauthn-framework": ">=3.3,<3.3.4", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", + "web-auth/webauthn-lib": ">=4.5,<4.9", "web-feet/coastercms": "==5.5", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", @@ -11799,7 +11804,7 @@ "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", "yiisoft/yii": "<1.1.29", - "yiisoft/yii2": "<2.0.50", + "yiisoft/yii2": "<2.0.49.4-dev", "yiisoft/yii2-authclient": "<2.2.15", "yiisoft/yii2-bootstrap": "<2.0.4", "yiisoft/yii2-dev": "<2.0.43", @@ -11885,7 +11890,7 @@ "type": "tidelift" } ], - "time": "2024-06-26T15:05:17+00:00" + "time": "2024-07-25T19:04:34+00:00" }, { "name": "sebastian/cli-parser", @@ -12805,16 +12810,16 @@ }, { "name": "spatie/backtrace", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23" + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9", + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9", "shasum": "" }, "require": { @@ -12852,7 +12857,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.6.1" + "source": "https://github.com/spatie/backtrace/tree/1.6.2" }, "funding": [ { @@ -12864,20 +12869,20 @@ "type": "other" } ], - "time": "2024-04-24T13:22:11+00:00" + "time": "2024-07-22T08:21:24+00:00" }, { "name": "spatie/error-solutions", - "version": "1.0.4", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/spatie/error-solutions.git", - "reference": "264a7eef892aceb2fd65e206127ad3af4f3a2d6b" + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/error-solutions/zipball/264a7eef892aceb2fd65e206127ad3af4f3a2d6b", - "reference": "264a7eef892aceb2fd65e206127ad3af4f3a2d6b", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67", "shasum": "" }, "require": { @@ -12930,7 +12935,7 @@ ], "support": { "issues": "https://github.com/spatie/error-solutions/issues", - "source": "https://github.com/spatie/error-solutions/tree/1.0.4" + "source": "https://github.com/spatie/error-solutions/tree/1.1.1" }, "funding": [ { @@ -12938,7 +12943,7 @@ "type": "github" } ], - "time": "2024-06-28T13:33:04+00:00" + "time": "2024-07-25T11:06:04+00:00" }, { "name": "spatie/flare-client-php", diff --git a/config/ticket.php b/config/ticket.php deleted file mode 100644 index e6af1ba39..000000000 --- a/config/ticket.php +++ /dev/null @@ -1,8 +0,0 @@ - env('TICKET_HOST'), - 'api_key' => env('TICKET_APIKEY'), - -]; diff --git a/config/trwl.php b/config/trwl.php index 2081a93d2..f591cee9c 100644 --- a/config/trwl.php +++ b/config/trwl.php @@ -38,10 +38,9 @@ 'subway' => env('BASE_POINTS_TRAIN_SUBWAY', 2), 'suburban' => env('BASE_POINTS_TRAIN_SUBURBAN', 3), 'ferry' => env('BASE_POINTS_TRAIN_FERRY', 3), - 'regional' => env('BASE_POINTS_TRAIN_REGIONAL', 5), - 'regionalExp' => env('BASE_POINTS_TRAIN_REGIONALEXP', 6), - 'express' => env('BASE_POINTS_TRAIN_EXPRESS', 10), - 'national' => env('BASE_POINTS_TRAIN_NATIONAL', 10), + 'regional' => env('BASE_POINTS_TRAIN_REGIONAL', 6), + 'regionalExp' => env('BASE_POINTS_TRAIN_REGIONALEXP', 8), + 'national' => env('BASE_POINTS_TRAIN_NATIONAL', 8), 'nationalExpress' => env('BASE_POINTS_TRAIN_NATIONALEXPRESS', 10), ] ], diff --git a/database/migrations/2024_07_07_000000_make_display_name_not_nullable_at_users.php b/database/migrations/2024_07_07_000000_make_display_name_not_nullable_at_users.php new file mode 100644 index 000000000..691364c61 --- /dev/null +++ b/database/migrations/2024_07_07_000000_make_display_name_not_nullable_at_users.php @@ -0,0 +1,25 @@ +where('name', null) + ->update(['name' => DB::raw('username')]); + + Schema::table('users', function(Blueprint $table) { + $table->string('name', 50)->nullable(false)->change(); + }); + } + + public function down(): void { + Schema::table('users', function(Blueprint $table) { + $table->string('name')->nullable()->change(); + }); + } +}; diff --git a/database/migrations/2024_07_07_000000_set_size_of_username_at_users.php b/database/migrations/2024_07_07_000000_set_size_of_username_at_users.php new file mode 100644 index 000000000..f2ba6809f --- /dev/null +++ b/database/migrations/2024_07_07_000000_set_size_of_username_at_users.php @@ -0,0 +1,20 @@ +string('username', 25)->change(); + }); + } + + public function down(): void { + Schema::table('users', function(Blueprint $table) { + $table->string('username')->change(); + }); + } +}; diff --git a/database/migrations/2024_07_14_081815_add_points_setting_to_users.php b/database/migrations/2024_07_14_081815_add_points_setting_to_users.php new file mode 100644 index 000000000..c617cf2bd --- /dev/null +++ b/database/migrations/2024_07_14_081815_add_points_setting_to_users.php @@ -0,0 +1,22 @@ +boolean('points_enabled')->default(true)->after('likes_enabled'); + }); + } + + public function down(): void { + Schema::table('users', function(Blueprint $table) { + $table->dropColumn('points_enabled'); + }); + } +}; diff --git a/database/seeders/CheckinSeeder.php b/database/seeders/CheckinSeeder.php index 3def01cdb..86ca37d8d 100644 --- a/database/seeders/CheckinSeeder.php +++ b/database/seeders/CheckinSeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Dto\Internal\CheckInRequestDto; use App\Enum\StatusTagKey; use App\Http\Controllers\Backend\Transport\TrainCheckinController; use App\Models\Event; @@ -17,16 +18,18 @@ class CheckinSeeder extends Seeder public function run(): void { foreach (User::all() as $user) { $trip = Trip::all()->random(); + + $dto = new CheckInRequestDto(); + $dto->setUser($user) + ->setTrip($trip) + ->setOrigin($trip->originStation)//Checkin from the first station... + ->setDeparture($trip->departure) + ->setDestination($trip->destinationStation)//...to the last station + ->setArrival($trip->arrival) + ->setEvent(random_int(0, 1) ? Event::all()->random() : null); + try { - $checkinResponse = TrainCheckinController::checkin( - user: $user, - trip: $trip, - origin: $trip->originStation, //Checkin from the first station... - departure: $trip->departure, - destination: $trip->destinationStation, //...to the last station - arrival: $trip->arrival, - event: random_int(0, 1) ? Event::all()->random() : null, - ); + $checkinResponse = TrainCheckinController::checkin($dto); $status = $checkinResponse['status']; StatusTag::factory(['status_id' => $status->id])->create(); } catch (Exception) { diff --git a/database/seeders/Constants/PermissionSeeder.php b/database/seeders/Constants/PermissionSeeder.php index 12fd0aa0b..375a0f6d3 100644 --- a/database/seeders/Constants/PermissionSeeder.php +++ b/database/seeders/Constants/PermissionSeeder.php @@ -14,25 +14,27 @@ class PermissionSeeder extends Seeder { public function run(): void { //Create roles - $roleAdmin = Role::updateOrCreate(['name' => 'admin']); - $roleEventModerator = Role::updateOrCreate(['name' => 'event-moderator']); - $roleOpenBeta = Role::updateOrCreate(['name' => 'open-beta']); - $roleClosedBeta = Role::updateOrCreate(['name' => 'closed-beta']); + $roleAdmin = Role::updateOrCreate(['name' => 'admin']); + $roleEventModerator = Role::updateOrCreate(['name' => 'event-moderator']); + $roleOpenBeta = Role::updateOrCreate(['name' => 'open-beta']); + $roleClosedBeta = Role::updateOrCreate(['name' => 'closed-beta']); + $roleDisallowManualTrips = Role::updateOrCreate(['name' => 'disallow-manual-trips']); //Create permissions - $permissionViewBackend = Permission::updateOrCreate(['name' => 'view-backend']); - $permissionViewEvents = Permission::updateOrCreate(['name' => 'view-events']); - $permissionAcceptEvents = Permission::updateOrCreate(['name' => 'accept-events']); - $permissionDenyEvents = Permission::updateOrCreate(['name' => 'deny-events']); - $permissionCreateEvents = Permission::updateOrCreate(['name' => 'create-events']); - $permissionUpdateEvents = Permission::updateOrCreate(['name' => 'update-events']); - $permissionDeleteEvents = Permission::updateOrCreate(['name' => 'delete-events']); - $permissionCreateManualTrip = Permission::updateOrCreate(['name' => 'create-manual-trip']); - $permissionViewActivity = Permission::updateOrCreate(['name' => 'view activity']); - $permissionViewEventHistory = Permission::updateOrCreate(['name' => 'view event history']); - $permissionCreateStations = Permission::updateOrCreate(['name' => 'create stations']); - $permissionUpdateStations = Permission::updateOrCreate(['name' => 'update stations']); - $permissionDeleteStations = Permission::updateOrCreate(['name' => 'delete stations']); + $permissionViewBackend = Permission::updateOrCreate(['name' => 'view-backend']); + $permissionViewEvents = Permission::updateOrCreate(['name' => 'view-events']); + $permissionAcceptEvents = Permission::updateOrCreate(['name' => 'accept-events']); + $permissionDenyEvents = Permission::updateOrCreate(['name' => 'deny-events']); + $permissionCreateEvents = Permission::updateOrCreate(['name' => 'create-events']); + $permissionUpdateEvents = Permission::updateOrCreate(['name' => 'update-events']); + $permissionDeleteEvents = Permission::updateOrCreate(['name' => 'delete-events']); + $permissionCreateManualTrip = Permission::updateOrCreate(['name' => 'create-manual-trip']); + $permissionViewActivity = Permission::updateOrCreate(['name' => 'view activity']); + $permissionViewEventHistory = Permission::updateOrCreate(['name' => 'view event history']); + $permissionCreateStations = Permission::updateOrCreate(['name' => 'create stations']); + $permissionUpdateStations = Permission::updateOrCreate(['name' => 'update stations']); + $permissionDeleteStations = Permission::updateOrCreate(['name' => 'delete stations']); + $permissionDisallowManualTrips = Permission::updateOrCreate(['name' => 'disallow-manual-trips']); //Assign permissions to admin role $roleAdmin->givePermissionTo($permissionViewBackend); @@ -48,6 +50,7 @@ public function run(): void { $roleAdmin->givePermissionTo($permissionCreateStations); $roleAdmin->givePermissionTo($permissionUpdateStations); $roleAdmin->givePermissionTo($permissionDeleteStations); + $roleDisallowManualTrips->givePermissionTo($permissionDisallowManualTrips); //Assign permissions to event-moderator role $roleEventModerator->givePermissionTo($permissionViewBackend); diff --git a/lang/ca.json b/lang/ca.json index c0b9cd601..001c329ae 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -1,16 +1,8 @@ { "about.express": "Intercity, Eurocity", - "about.events": "Què són els esdeveniments?", - "about.faq-heading": "Preguntes freqüents", "about.feature-missing-heading": "Com puc informar d'errors o fer suggeriments de millora?", - "about.heading": "Sobre Träwelling", "about.international": "Intercity Express, TGV, Railjet", - "about.name": "El nom és una referència al conegut «Senk ju for träwelling wis Deutsche Bahn\"», que hauríeu d'haver escoltat en gairebé tots els trens de llarga distància de Deutsche Bahn.", - "about.name-heading": "D'on ve el nom?", - "about.no-train": "Utilitzem una interfície de Deutsche Bahn, on no totes les ofertes es mostren directament. Malauradament, no podem fer-ne massa si el vostre tren no hi apareix.", - "about.points1": "Els punts consisteixen en la classe de producte i la distància del recorregut.", "about.suburban": "S-Bahn, ferri", - "about.who2": "A GitHub podeu veure la llista de persones que han contribuït al codi.", "user.login-credentials": "Correu electrònic o nom d'usuari", "user.register": "Registra'm", "menu.login": "Inicia sessió", @@ -20,13 +12,8 @@ "refresh": "Recarrega", "menu.show-more": "Mostra'n més", "about.block1": "Träwelling és un servei gratuït que permet registrar els viatges en transport públic i fer saber on sou a les vostres amistats. En resum, podeu registrar els trens i obtenir punts per fer-ho.", - "about.no-train-heading": "Per què no apareix el meu tren?", - "about.points-heading": "Com es calculen els punts?", - "about.productclass": "classe de producte", "about.regional": "Tren regional/-express", "about.tram": "Tramvia, autobús, metro", - "about.who0": "Träwelling és un projecte de codi obert.", - "about.who1": "Des del 2013, diverses persones han estat desenvolupant el projecte. Algunes, només correccions d'errors; altres, canvis importants.", "user.login": "Inicia sessió", "error.login": "Credencials incorrectes", "settings.title-loginservices": "Serveis connectats", diff --git a/lang/cs.json b/lang/cs.json index 50065d044..2dec9721f 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -1,31 +1,13 @@ { - "about.basepoints": "Základní body", "about.express": "InterCity, EuroCity", - "about.events": "Co jsou události?", - "about.events.description1": "Träwelling spojuje dohromady lidi, kteří využívají stejnou hromadnou dopravu. Někdy takoví lidé spolu jedou na stejnou akci, aniž by to tušili!", - "about.events.description2": "Proto jsme vytvořili funkci Události. Kdokoliv může přidat událost v určitém časovém okně; jakmile je akceptována, všichni uživatelé můžou své hlášení cesty propojit s touto událostí.", - "about.events.description4": "Události jsou něco speciálního. Proto jsme se rozhodli odmítat návrhy na menší lokální události, jako jsou třeba vánoční trhy nebo poutě. Jako vždy, i zde jsou nějaké výjimky.", - "about.faq-heading": "Často kladené dotazy", "about.feature-missing": "V našem GitHub repozitáři můžete hlásit chyby nebo navrhovat nové funkce.", "about.feature-missing-heading": "Jak nahlásím chybu nebo vylepšení?", - "about.heading": "O Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "Jméno služby je narážka na známé \"Senk ju for träwelling wis Deutsche Bahn\", které byste měli slyšegt při skoro každé cestě dálkovým vlakem Deutsche Bahn.", - "about.name-heading": "Odkud se vzalo jméno služby?", - "about.no-train": "Používáme rozhraní Deutsche Bahn, které nezobrazuje úplně všechny nabídky. Pokud se Vám nedaří Váš vlak nalézt, nic moc s tím bohužel udělat nemůžeme.", - "about.no-train-heading": "Proč nemůžu najít můj vlak?", - "about.points-heading": "Jak se počítají body?", - "about.points1": "Body se skládají z třídy prostředku a uražené délce vaší cesty.", - "about.productclass": "třída prostředku", "about.regional": "Regionální vlak/-expres", "about.suburban": "S-Bahn, Trajekt", "about.tram": "Tramvaj / rychlodráha, bus, metro", - "about.who0": "Träwelling je open source projekt.", - "about.who1": "Od roku 2013 se na vývoji projektu podíleli různí lidé. Z části jen opravování chyb, z části na velkých změnách.", - "about.who-heading": "Kdo vyvíjí Träwelling?", "admin.greeting": "Ahoj", "admin.usage": "Využití", - "login-required": "(Vyžaduje přihlášení)", "admin.usage-board": "Tabule využití", "admin.select-range": "Vyberte rozsah", "admin.checkins": "Hlášení", @@ -77,10 +59,6 @@ "controller.social.create-error": "Při vytváření účtu nastala chyba.", "controller.social.delete-set-email": "Než odstraníte poskytovatele SSO, musíte si kvůli zabránění ztrátě přístupu do účtu nastavit e-mailovou adresu.", "about.block1": "Träwelling je bezplatná služba, se kterou můžete hlásit svým přátelům, kde se zrovna nacházíte, a kde můžete zaznamenávat své cesty veřejnou dopravou. Zkrátka zaznamenávejte cesty vlakem a získávejte body.", - "about.calculation": "Vzdálenost je zaokrouhlena na nejbližších 10 km a následně podělena 10. Poté jsou přidány základní body.
Když urazíte 143 km vlakem ICE, tak získáte 10 + zaokrouhlených (143/10) = 10 + 15 = 25 Bodů, za cestu S-Bahnem dlouhou 8 km získáte 2 + zaokrouhlených (8/10) = 2 + 1 = 3 Bodů.
Chceme, abyste jízdu nahlásili co nejdříve, abychom měli vždy nejaktuálnější stav o Vaší cestě. Proto také získáte plný počet bodů pouze v případě, že jízdu nahlásíte během cesty nebo 20 minut před aktuálním odjezdem (odjezd podle jízdního řádu + zpoždění). Pokud jízdu nahlásíte 1 hodinu před nebo po cestě, stále dostanete 1/4 z plného počtu bodů. Když cestu nahlásíte dříve nebo později v přípoji, získáte plné kilometry a hodiny, ale jen jeden bod z lítosti.", - "about.events.description3": "Pokud chcete přidat událost, ujistěte se, že z ní bude mít Träwelling komunita nějaký přínos. Takovými událostmi jsou např. ty od Evropské vlakové komunity, jako je poslední cesta vlaku, hackovací události, např. GPN nebo ty z LGBTQ scény, jako je Cologne Pride.", - "about.who2": "Na GitHubu si můžete prohlédnout seznam přispěvatelů.", - "about.who3": "Někteří lidé navíc pomáhali s alfa verzí. Ty lze najít zde.", "user.email.new": "Nová e-mailová adresa", "user.please-check": "Než budete pokračovat, klikněte na odkaz v ověřovacím e-mailu.", "user.home-not-set": "Nenastavili jste si ještě domovskou stanici.", diff --git a/lang/de.json b/lang/de.json index 476becf2d..aec6e0964 100644 --- a/lang/de.json +++ b/lang/de.json @@ -1,30 +1,13 @@ { - "about.basepoints": "Basispunkte", "about.block1": "Träwelling ist ein kostenloser Check-in Service, mit dem du deinen Freunden mitteilen kannst, wo du gerade mit öffentlichen Verkehrsmitteln unterwegs bist und Fahrtenbuch führen kannst. Kurz gesagt: Man kann in Züge einchecken und bekommt dafür Punkte.", - "about.calculation": "Die Entfernung wird auf die nächsten 10km gerundet und dann durch 10 geteilt. Anschließend werden die Basispunkte addiert.
Eine ICE-Reise von 143km bringt dir also 10 + aufrunden(143/10) = 10 + 15 = 25 Punkte, für eine S-Bahn-Fahrt von 8km gibt es 2 + aufrunden(8/10) = 2 + 1 = 3 Punkte.
Wir möchten, dass Du möglichst zeitnah eincheckst, damit die Plattform immer einen aktuellen Stand Deiner Reisen hat. Darum gibt es die vollen Punkte nur, wenn Du während Deiner Reise oder 20min vor Live-Abfahrt (Plan-Abfahrt + Verspätung) eincheckst. Wer 1h vor oder nach dem Reisezeitraum eincheckt, bekommt immerhin noch 1/4 der Punkte. Wenn Du noch früher oder später in eine Verbindung eincheckst, bekommst Du nur einen Mitleidspunkt. Die vollen Kilometer und Stunden werden Dir in jedem Fall gutgeschrieben.", "about.express": "InterCity, EuroCity", - "about.faq-heading": "Häufig gestellte Fragen", "about.feature-missing": "Wenn Du ein Feature vorschlagen möchtest oder einen Bug gefunden hast melde ihn bitte direkt in unserem GitHub-Repository.", "about.feature-missing-heading": "Wie melde ich Fehler oder Verbesserungsvorschläge?", - "about.heading": "Was ist Träwelling?", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "Der Name ist eine Anspielung auf das allseits bekannte \"Senk ju for träwelling wis Deutsche Bahn\", was man eigentlich in fast jedem Fernverkehrszug der Deutschen Bahn gehört haben sollte.", - "about.name-heading": "Woher kommt der Name?", - "about.no-train": "Wir verwenden eine Schnittstelle der Deutschen Bahn, bei der nicht alle Angebote direkt dargestellt werden. Leider können wir da auch nicht viel dran tun, wenn dein Zug nicht dabei ist.", - "about.no-train-heading": "Warum wird mein Zug nicht aufgelistet?", - "about.points-heading": "Wie werden Punkte berechnet?", - "about.points1": "Die Punkte setzen sich aus der Produktklasse und der Entfernung deiner Reise zusammen.", - "about.productclass": "Produktklasse", "about.regional": "Regionalbahn/-express", "about.suburban": "S-Bahn, Fähre", "about.tram": "Tram / Stadtbahn, Bus, U-Bahn", - "about.who0": "Träwelling ist ein Open-Source-Projekt.", - "about.who1": "Seit 2013 entwickeln verschiedene Personen am Projekt. Teils nur Bugfixes, teils größere Änderungen.", - "about.who2": "Auf GitHub kannst du dir eine Liste von zum Code Beitragenden ansehen.", - "about.who3": "Zusätzlich haben ein paar Personen bei der Alpha-Version unterstützt. Diese findest du hier.", - "about.who-heading": "Wer entwickelt Träwelling?", "admin.greeting": "Hallo", - "login-required": "(Login benötigt)", "admin.usage": "Nutzung", "admin.usage-board": "Nutzungsauswertung", "admin.select-range": "Bereich auswählen", @@ -106,6 +89,7 @@ "user.home-not-set": "Du hast noch keinen Heimatbahnhof festgelegt.", "user.private-profile": "Privates Profil", "user.likes-enabled": "\"Gefällt mir\"-Angaben anzeigen", + "user.points-enabled": "Punkte und Bestenliste anzeigen", "user.liked-status": "gefällt dieser Status.", "user.liked-own-status": "gefällt der eigene Status.", "user.invalid-mastodon": ":domain scheint keine Mastodon-Instanz zu sein.", @@ -527,6 +511,11 @@ "user.already-unmuted": "Der Benutzer :username ist nicht stummgeschaltet.", "user.unmute-tooltip": "Benutzer nicht mehr stummschalten", "dashboard.future": "Deine Check-ins in der Zukunft", + "dashboard.empty": "Dein dashboard wirkt noch etwas leer.", + "dashboard.empty.teaser": "Wenn du möchtest, kannst du anderen Träwellern folgen, um ihre Check-ins zu sehen!", + "dashboard.empty.discover1": "Neue Leute kannst du unter", + "dashboard.empty.discover2": "oder", + "dashboard.empty.discover3": "(achtung, lange Wartezeit) entdecken", "description.profile": ":username ist bereits :kmAmount Kilometer in :hourAmount Stunden in öffentlichen Verkehrsmitteln unterwegs gewesen.", "description.status": "Die Reise von :username von :origin nach :destination am :date in :lineName.", "description.leaderboard.main": "Die Top Träweller der letzten 7 Tage.", @@ -534,15 +523,9 @@ "description.en-route": "Übersichtskarte über alle Träweller, welche gerade auf der Welt unterwegs sind.", "leaderboard.notice": "Die hier angezeigten Daten basieren auf den Check-ins der letzten 7 Tage. Aktualisierungen können bis zu fünf Minuten dauern.", "localisation.not-available": "Entschuldigung, dieser Inhalt steht aktuell leider nicht auf deiner Sprache zur Verfügung.", - "support": "Support", - "support.create": "Support-Anfrage erstellen", - "support.submit": "Support-Anfrage abschicken", - "support.email-required": "Du benötigst eine verifizierte E-Mail-Adresse, um eine Support-Anfrage stellen zu können. Nur so können wir dir auch antworten.", "go-to-settings": "Zu den Einstellungen", "subject": "Betreff", "how-can-we-help": "Wie können wir helfen?", - "support.answer": "Wir werden dir so schnell wie möglich antworten. Die Antwort erhältst du an deine E-Mail-Adresse :address.", - "support.success": "Deine Anfrage ist bei uns eingegangen (Ticket #:ticketNumber). Wir werden dir so schnell wie möglich per E-Mail antworten.", "request-time": "abgefragt um :time", "settings.request.accept": "Follower akzeptieren", "settings.follower.following-since": "Folgt seit", @@ -554,6 +537,8 @@ "checkin.points.full": "Du hättest :points Punkte bekommen können.", "checkin.points.forced": "Du hast für diesen Check-in keine Punkte erhalten, da du ihn forciert hast.", "checkin.conflict": "Es existiert bereits ein paralleler Check-in.", + "checkin.success.body": "Du hast dich erfolgreich eingecheckt!", + "checkin.success.body2": "Du reist :distance km mit :lineName von :origin nach :destination.", "checkin.success.title": "Erfolgreich eingecheckt!", "checkin.conflict.question": "Möchtest du trotzdem einchecken? Hierfür bekommst du keine Punkte, aber deine persönliche Statistik wird dennoch aktualisiert.", "generic.error": "Fehler", @@ -595,26 +580,7 @@ "overlapping-checkin.force-yes": "Ja, erzwingen.", "overlapping-checkin.force-no": "Nein, nichts machen.", "report-bug": "Fehler melden", - "to-support": "Zum Support", "request-feature": "Funktion wünschen", - "about.events": "Was sind Veranstaltungen?", - "about.events.description1": "Träwelling bringt Menschen zusammen, die gemeinsam öffentliche Verkehrsmittel nutzen. Manchmal fahren sie sogar zu derselben Veranstaltung, ohne voneinander zu wissen!", - "about.events.description2": "Deswegen haben wir das Veranstaltungen-Feature integriert. Jede*r kann über unser Formular eine Veranstaltung für einen bestimmten Zeitraum anlegen und alle Benutzer*innen von Träwelling können ihre Check-ins dann mit dem Event verknüpfen.", - "about.events.description3": "Wenn du ein Event anlegen möchtest, beachte bitte, dass Veranstaltungen einen Mehrwert für die gesamte Community von Träwelling haben sollen. Dazu gehören Events aus dem Eisenbahnwesen wie bspw. die letzte Fahrt des Metropolitan, Events von Hackerspaces (z.B. die GPN), oder aus der LGBTQ-Szene wie die Cologne Pride.", - "about.events.description4": "Veranstaltungen sollen etwas Besonderes sein. Deswegen haben wir uns entschieden, kleine lokale Veranstaltungen wie Weihnachtsmärkte und Stadtfeste prinzipiell abzulehnen. Ausnahmen bestätigen hierbei die Regel.", - "about.missing-verification-email": "Warum erhalte ich keine Bestätigungsmail?", - "about.missing-verification-email.description": "Bitte schaue als erstes nach, ob die E-Mail im Spamordner liegt.", - "about.missing-verification-email.description2": "Wenn die E-Mail nicht im Spamordner ist, füge bitte ':email' zu deinen Kontakten hinzu und fordere die E-Mail erneut an.", - "about.missing-verification-email.description3": "Sollte die E-Mail weiterhin nicht ankommen, kontaktiere uns bitte über unser Kontaktformular.", - "about.missing-verification-email.description4": "Bitte beachte, dass es in der Vergangenheit öfters zu Problemen bei Microsoft-Adressen (z.B. Live, Outlook, Hotmail, ...) kam.", - "about.missing-verification-email.description5": "Probiere in dem Fall bitte zunächst eine E-Mail bei einem anderen Anbieter aus.", - "about.points-real-time": "Wenn deine Verbindung verspätet ist solltest du darauf achten, vor der reellen Abfahrtszeit einzuchecken, da wir nach der reellen Abfahrt keine Echtzeitinformationen über die Fahrt mehr erhalten können.", - "about.dev": "Ich möchte auch an Träwelling mitentwickeln!", - "about.dev.1": "Träwelling ist ein Open Source Projekt und jeder kann mitentwickeln.", - "about.dev.2": "Du findest alle Informationen dazu auf unserer GitHub-Seite.", - "about.dev.3": "Wir freuen uns über jeden Beitrag - erstelle einfach einen PullRequest!", - "about.dev.4": "Du kannst entweder an eigenen Ideen arbeiten oder dir eine Aufgabe aus unseren Issues auf GitHub raussuchen.", - "about.dev.5": "Wenn du Fragen hast, kannst du dich gerne auf unserem Discord-Server an uns wenden: :link", "other": "Sonstige", "exit": "Ausstieg", "platform": "Gleis", @@ -665,7 +631,6 @@ "scopes.write-followers": "Folgen-Anfragen bestätigen und Follower entfernen", "scopes.write-blocks": "User blocken und entblocken, muten und entmuten", "scopes.write-event-suggestions": "Events in deinem Namen vorschlagen", - "scopes.write-support-tickets": "Support-Anfragen in deinem Namen erstellen", "scopes.read-settings": "deine Einstellungen, E-Mail-Adresse, etc. sehen", "scopes.write-settings-profile": "dein Profil bearbeiten", "scopes.read-settings-profile": "deine Profildaten sehen, bspw. E-Mail-Adresse", @@ -688,7 +653,7 @@ "events.disclaimer.warranty": "Träwelling übernimmt keine Gewähr auf die Korrektheit oder Vollständigkeit der Daten.", "user.mapprovider": "Karten-Anbieter", "user.timezone": "Zeitzone", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "map-providers.open-railway-map": "Open Railway Map", "stationboard.check-tweet": "Twittern", "active-journeys": "aktive Fahrt|aktive Fahrten", @@ -792,6 +757,9 @@ "trip_creation.limitations.3": "Der Trip wird öffentlich erstellt - wenn du also eincheckst, kann jeder, der deinen Status sehen kann, ebenfalls in diesen Trip einchecken.", "trip_creation.limitations.4": "Der Betreiber kann in diesem Formular nicht festgelegt werden (über API möglich)", "trip_creation.limitations.5": "Es gibt keine sichtbaren Fehlermeldungen für dieses Formular. Wenn also beim Absenden nichts passiert, liegt irgendwo ein Fehler vor.", + "trip_creation.limitations.6": "Es sind nur die auswählbaren Arten von Fahrten erwünscht (das heißt: Fahrten mit Auto, Fahrrad, zu Fuß, etc. sind nicht erlaubt und werden gelöscht) Siehe auch:", + "trip_creation.limitations.6.rules": "Regeln", + "trip_creation.limitations.6.link": "https://help.traewelling.de/features/manual-trips/#info", "action.error": "Diese Aktion konnte leider nicht ausgeführt werden. Bitte versuche es später noch einmal.", "action.like": "Status liken", "action.dislike": "Status nicht mehr liken", diff --git a/lang/de_by.json b/lang/de_by.json index f7e24cb6e..914b300df 100644 --- a/lang/de_by.json +++ b/lang/de_by.json @@ -1,13 +1,10 @@ { "about.express": "IntrCity, EuroCity", - "about.faq-heading": "Heifig gstellta Froga", "about.feature-missing": "Wenn du a nuie Funktion vorschlaga meachtsch odr an Feahlr gfunda hosch, meldn bitte direkt in eiserm GitHub-Repository.", "about.feature-missing-heading": "Wia ka i Feahlr odr Verbessrungsvorschläg macha?", - "about.heading": "Über Träwelling", "about.international": "IntrCityExpress, TeScheWeh, RailJet", "about.suburban": "S-Bahn, Böötla", "about.tram": "Tram / Stadtbahn, Bus, U-Bahn", - "about.who-heading": "Wer entwicklat Träwelling?", "admin.greeting": "Servus", "admin.usage": "Nutzung", "admin.usage-board": "Nutzungauswertung", @@ -24,16 +21,11 @@ "controller.social.delete-never-connected": "Du hosch koan Social-Login-Provider.", "controller.social.delete-set-password": "Bevor du an SSO-Provider löschsch, muasch du a Passwort feschdleaga, um di id auszumsperra.", "controller.social.deleted": "Die Verbindung isch aufghoba worda.", - "about.productclass": "Produktklassn", "about.regional": "Regionalbahn/-express", - "about.who": "An Träwelling wird seit 2013 vo immer andre Leit entwicklat. Toals nur Bugfixes, toals greßre Ändrunga. Gnaure Infos findsch in dr", "controller.social.already-connected-error": "Der Account is bereids mit am andara Nutzer verknüpft.", "controller.social.delete-set-email": "Bevor du an SSO-Provider löschsch, musch du a E-Mail Adressn festleaga, um di id auszumsperra.", - "about.basepoints": "Basispunkte", "about.block1": "Träwelling is a kostaloser Check-in Service, mit deam du deine Freind mitteila kahsch, wo de grad mit am öffetlicha Verkehrsmittl unterweags bisch und Fahrtabuach führa kahsch. Kurz gsait: Ma ka in Ziagla eichecka und kriagt a paar Punkte dafiar.", - "about.calculation": "Die Entfernung werd uf dia nächste 10km grundet und dann dura 10 toilt. Danach werat die Basispunkte dazua greachnet.
A 143km lange Fahrt mit am ICE bringt dr also 10 + aufrunda(143/10) = 10 + 15 = 25 Punkte, für a S-Bahn-Farht vo 8km gaits 2 + aufrunda(8/10) = 2 + 1 = 3 Punkte.
Mir mechtat, dass de recht zeitig eichecksch, damit dia Plattform immer an aktuella Stand vo deiner Reisa hot. Desweaga gaits dia volla Punkte bloß, wenn de während deiner Reisen oder zwanzg Minuta vor Live-Abfahrt (Plan-Abfahrt + Verspätung) eichecksch. Wer a Stund vor oder nachm Reisezeitraum eicheckt, kriagt immerhin no a viartele der Punkte. Wenn da no frianer eichecksch, kriagst zwar die volla Kilometer und Stunda aufgrechnat, aber bloß an Mitleidspunkt.", "controller.transport.no-station-found": "Fiar dia Suach isch koa Haltestelln gfunda wora.", - "about.name": "Dr Name is a Anspielung af des allseits bekannte \"Senk ju for träwelling wis Deutsche Bahn\", woas ma jo oigetlich in so guat wie jedem Fernvekehrszüagli dr Deitscha Bahn ghert ham sutt.", "controller.transport.overlapping-checkin": "Du hosch schu an Check-In in dr Verbindung :linename.", "controller.user.follow-404": "Dean Follow hots gar id gea.", "controller.transport.not-in-stopovers": "Dia Start-ID isch id in deane Zwischastops.", @@ -42,17 +34,12 @@ "controller.transport.checkin-heading": "Oicheckt", "controller.status.status-not-found": "Status id gfunda", "controller.transport.social-post": "I bi grad in dr :lineName noch :Destination! #NowTräwelling|I bi grad in dr Linie :lineName noch :Destination! #NowTräwelling ", - "about.name-heading": "Woher kimmt dr Name?", - "about.no-train-heading": "Warum werd mei Ziagla id azoigt?", - "about.points-heading": "Wia werad die Punkte usgreachnat?", - "about.points1": "Dia Punkte setzt sich us dr Produktklassn und dr Entfernung diner Reisen zamme.", "controller.status.create-success": "Status erstellt.", "controller.status.delete-ok": "Status glöscht.", "controller.status.email-resend-mail": "Bestätigungslink schicka", "controller.status.export-invalid-dates": "Des sand koane richtige Data.", "controller.status.export-neither-business": "Du kahsch id Privat und Business-Trips gleichzeitig abwähla.", "controller.status.like-deleted": "Like isch glöscht wora.", - "about.no-train": "Mir verwendat a Schnittstelln vo dr Deitscha Bahn, bei der id alle Angebote direkt dargstellt werad. Leider könnet mir da aber au id vui doa, wenn dei Ziagla id mit dabei is.", "controller.transport.checkin-ok": "Du hosch erfolgreich ind :lineName oicheckt!|Du hosch erfolgreich ind Linie :lineName oicheckt!", "controller.transport.no-name-given": "Du muasch an Stationsnoma ageah!", "controller.transport.social-post-for": "fiar #:hashtag", @@ -442,7 +429,6 @@ "settings.follower.no-followings": "Du folgsch koam.", "generic.change": "Ändra", "how-can-we-help": "Wie kennat mer helfa?", - "support.success": "Dei Afrogn isch bei eis eiganga (Ticket #:ticketNumber). Mir werat dir so schnell wia meglich per E-Mail antworta.", "christmas-mode": "Weihnachtsmodus", "time.hours.short": "Std.", "time.days.short": "Tg.", @@ -456,37 +442,22 @@ "settings.prevent-indexing": "Suachmaschinaindizierung verhindra", "status.visibility.4.detail": "Nur fiar agmeldete Nutzer sichtbar", "stats.time": "Dei tägliche Reisezeit", - "support": "Support", - "support.submit": "Support-Afrogn aschicka", - "about.dev.5": "Wennd Froga hosch, kasch di gera auf eiserm Discord-Server an eis wenda: :link", "settings.visibility.hide": "Check-Ins automatisch ausblenda noch", "stationboard.check-chainPost": "An de letzte gpostete Checkin ahänga", "profile.no-visible-statuses": "Dia Reisa vu :username sind id sichtbar.", "status.visibility.4": "Nur agmeldete Nutzer", "settings.delete-account.more": "Ableana & Account me löscha", "status.update.success": "Dei Status isch aktualisiert wora.", - "support.email-required": "Du bnötigsch a verifizierte E-Mail Adressn, um a Support-Afrogn stella zum kenna. Nur so kennat mir dir au antworta.", "go-to-settings": "Zu de Eischtellunga", "subject": "Betreff", - "support.answer": "Mir werat dir so schnell wia meglich antworta. Dia Antwort erhältsch an dei E-Mail Adressn :address.", - "support.privacy.description": "Dei User-ID, dei Usernoma und dei E-Mail Adressn werat mit deiner Afrogn in eiserm Ticketsystem erfassat.", "checkin.points.could-have": "Du hättsch mehr Punkte kriaga kenna, wennd nähr an dr echta Abfahrtszeit eicheckt hättsch!", "christmas-mode.enable": "Weihnachtsmodus für dia Session aktiviera", "time.months": "Monet", "time.years": "Johr", "experimental-feature": "Experimentelle Funktion", - "about.events.description1": "Träwelling bringt Menscha zam, dia gemeinsam öffetliche Verkehrmittl nutzat. Manchmoi fahret se sogar zum gleicha Feschtla, ohne vo einander zum wissa!", - "about.events.description2": "Desweaga hend mir des Feschtla-Feature eibaut. Jeade*r ka iaber eiser Formular a Feschtla fiar an bschdimmta Zeitraum aleaga und alle Bnutzer*inna vu Träwelling kennat ihre Check-Ins dann mit deam Feschtla verknüpfa.", - "about.events.description3": "Wennd a Feschtla aleaga meachtsch, pass auf, dass dia Feschtla an Mehrwert fiar dia gsamte Community vu Träwelling heba soll. Dazu gherat Feschtla aus am Eisabahweasa wia z.B. dia letschte Fahrt vom Metropolitan, Feschtla vu Hackerspaces (z.B. dia GPN), odr aus der LGBTQ-Szenen wia dia Cologne Pride.", - "about.dev.1": "Träwelling isch a Open Source Projekt und jeadr ka mitentwickla.", "year-review.teaser": "Des Joahr noigat sich am End und mir hend vui zamma erleabt. Schau dir jetzt dein Träwelling Johresruckblick a:", "overlapping-checkin": "Iaberschneidende Fahrt", "overlapping-checkin.description": "Dei Fahrt hot id gspeichert wera könna, da se mit deim Check-In in :lineName iaberlappt.", - "about.who0": "Träwelling isch a Open-Source-Projekt.", - "about.who1": "Seit 2013 entwicklat verschiadne Persona am Projekt. Teils bloß Bugfixes, teils greeßre Ändrunga.", - "about.who2": "Auf GitHub kasch dir a Lischtn vo zum Code Beitragende aschaua.", - "about.who3": "Zuasätzlich hend a paar Persona bei dr Alpha-Version unterstützt. Dia findsch du do.", - "login-required": "(Login benötigt)", "transport_types.taxi": "Taxi", "christmas-mode.disable": "Weihnachtsmodus für dia Session deaktiviera", "merry-christmas": "Mir wünschat dir a frohe Weihnachtszeit!", @@ -496,13 +467,7 @@ "overlapping-checkin.force-yes": "Jo, erzwinga.", "overlapping-checkin.force-no": "Nei, nix doa.", "report-bug": "Feahler melda", - "to-support": "Zum Support", "request-feature": "Funktion wünscha", - "about.missing-verification-email.description": "Bitte schau erscht amoi noch, ob dia E-Mail im Spamordner flackat.", - "about.missing-verification-email.description2": "Wenn dia E-Mail id im Spamordner isch, fiag bitte ':email' zu deine Kontakte dazua und forder dia E-Mail no amoi a.", - "about.missing-verification-email.description3": "Sutt dia E-Mail weiter id akimma, kontaktier eis bitte iabr eiser Kontaktformular.", - "about.missing-verification-email.description5": "Probier in deam Fall bitte erst a E-Mail vu am andra Abiater aus.", - "about.missing-verification-email.description4": "Bitte pass auf, dass es in dr Vergangahoit öfter zu Probleama bei Microsoft-Adressa (z.B. Live, Outlook, Hotmail, ...) komma isch.", "support.create": "Support-Afrogn erstella", "stats.stations": "Stationskart", "time.minutes.short": "Min.", @@ -536,14 +501,6 @@ "experimental-features": "Experimentelle Funktiona", "data-may-incomplete": "Agaba kennat uvollständig odr feahlerhaft sei.", "empty-en-route": "Aktuell sand koane Träweller unterweags.", - "about.events": "Was sand Feschtla?", - "about.events.description4": "Feschtla suttet ebbes Bsondrs sei. Desweaga hend mir eis dafir entschiada, kloine lokale Feschtla wia Christkindlsmärkt und Stadtfescht prinzipiell azumleahna. Ausnahma bschdädigat dia Regel.", - "about.missing-verification-email": "Warum kriag i koa Bschdädigungsmail?", - "about.points-real-time": "Wenn dei Verbindung z'spät isch, solltsch drauf achta, vor dr eachte Abfahrtszeit eizumchecka, da mir noch dr Abfahrt koane Echtzeitinformationa meh iabr dia Fahrt kriaga kennat.", - "about.dev": "I mog au an Träwelling mitentwickla!", - "about.dev.2": "Alle Informationa findsch auf eisrer GitHub-Seitn.", - "about.dev.3": "Mir gfreinat eis iaber jedn Beitrag - erstell oifach an Pull Request!", - "about.dev.4": "Du kasch entweadr an oigene Ideea schaffa odr dir a Aufgob aus eisre Issues auf GitHub raussuacha.", "platform": "Gleis", "real-time-last-refreshed": "Echtzeitdata zletscht aktualisierat", "help": "Hilfe", diff --git a/lang/de_he.json b/lang/de_he.json index f712dafe8..e891e6824 100644 --- a/lang/de_he.json +++ b/lang/de_he.json @@ -1,25 +1,12 @@ { - "about.basepoints": "Basispunkte", "about.block1": "Träwelling issn kostenloser Scheck-in Service, mim du dein Kumbels middeile koannschd, wu du groad mid öffentlischen Verkehrsmiddeln unnerwejgs bischd unn Fahrtenbuch führe koannschd. Korz gsoaht: Mar koann in Zigg einschecke unn krieht defeer Punkte.", - "about.calculation": "Die Entfernung wird uff die neegschde 10km gerundet unn doann dorsch 10 geteilt. Anschließend werrn die Basispunkte addiert.
E ICE-Reise vunn 143km bringt dir also 10 + aufrunde(143/10) = 10 + 15 = 25 Punkte, für e S-Bahn-Fahrt vunn 8km gibt 's 2 + aufrunde(8/10) = 2 + 1 = 3 Punkte.
Mer wolle, dassde meglischst zeitnah einscheckst, damit die Pladdform als en aktuelle Stand Doiner Reise hot. Darum gibt 's die volle Punkte blous, wannde während Doiner Reis orrer 20min vor Live-Abfahrt (Plan-Abfahrt + Verspätung) einscheckst. Wer 1h vor orrer noach dem Reisezeitraum einscheckt, krieht immerhin noch 1/4 der Punkte. Wannde noch friher orrer spärer in e Verbindung einscheckst, krieschde die volle Kilometer unn Schdunne gutgeschriebe, awwer blous en Mitleidspunkt.", "about.express": "InnerCity, EuroCity", - "about.faq-heading": "Häufisch geschdellt Frooche", "about.feature-missing": "In der Version vunn Träwelling fange mer emol goanz vunn vorne an, debei koann 's soi, dass wenig-verwendete Funkzionen noch nedd implementiert sinn. Wannde ein Feature vorschlage willschd, schreib oafach e E-Mail an", "about.feature-missing-heading": "Doa fehlt ebbes! Wieso wurd dess Feature entfernt?", - "about.heading": "Iwwer Träwelling", "about.international": "InnerCityExpress, TGV, RailJet", - "about.name": "Der Noame is e Anspielung uffs allseits bekannte \"Senk ju for träwelling wis Deutsche Bahn\", woas mar eischendlisch in faschd jedem Fernverkehrszug der Deitsche Bahn gehört hawwe sollt.", - "about.name-heading": "Woher kummt der Noame?", - "about.no-train": "Mer verwenne e Schniddstell der Deitsche Bahn, bei'de nedd all Oagebode direkt gezeigt werrn. Leider kenne mer doa aa nedd veel drou duun, woann dein Zug nedd debei is.", - "about.no-train-heading": "Warum wird mein Zug nedd uffgelistet?", - "about.points-heading": "Wie werrn Punkte bereschnet?", - "about.points1": "Die Punkte hogge sisch ausde Produktklass unn der Entfernung doiner Reis zamme.", - "about.productclass": "Produktklass", "about.regional": "Regionalbahn/-express", "about.suburban": "S-Bahn, Fähre", "about.tram": "Tram / Stadtbahn, Bus, U-Bahn", - "about.who": "An Träwelling wird seit 2013 vunn als oannere Leit entwickelt. Teils blous Bugfixes, teils grissere Änderunge. Genaaere Infos findschde inde", - "about.who-heading": "Wer entwickelt Träwelling?", "admin.greeting": "Ei wie", "admin.usage": "Nutzung", "admin.usage-board": "Nutzungsauswertung", @@ -431,6 +418,5 @@ "welcome": "Willkumme bei Träwelling!", "email.verification.required": "Um Träwelling in vollem Umfang nutze zu kenne, muschde noch dei E-Mail Adresse bestätige.", "email.verification.sent": "Um die Änderung doiner E-Mail Adresse abzuschließe muschde noch uff de Link inde E-Mail klicke, welsche mer dir groad gschickt hawwe.", - "about.events.description1": "Träwelling bringt annern Leit zusamme welche gerad die Bahn nutze. Manchmal fahr'n die Leit in de selbe Bahn ohne es zu wisse!", "about.events": "Was steht aa?" } diff --git a/lang/de_pfl.json b/lang/de_pfl.json index 9cf112356..8fea10380 100644 --- a/lang/de_pfl.json +++ b/lang/de_pfl.json @@ -1,25 +1,12 @@ { - "about.basepoints": "Basispungde", "about.block1": "Träwelling is ään koschdeloser Check-In Dinscht, mit wellem du deine Freinde saan kanschd, wo du grad mit de öffendelische Verkehrmiddel unnerwegs bisch und Fahrdebuch fehre kannsch. Korz gesaa: mer kann in Ziech inchecke unn bekommt dodefihr Pungde.", - "about.calculation": "Die Entfernung werd uff die nächschde 10 Kilomeder gerund un durch 10 gedeeld. Denooch werre die Basispungde zammegezählt.
En ICE-Rääs vun 143 Kilomeder bringt dir dodemit 10 + uffrunde(143/10) = 10 + 15 = 25 Pungde, fer e S-Bahn-Rääs vun 8 Kilomeder gebbts 2 + uffrunde(8/10) = 2 + 1 = 3 Pungde.
Mer wolle, dass de meeglichst dabber inchecke duscht, damit die Plattform immer e akdueller Stand vun deiner Rääs hat. Desdewege gebbts die volle Pungde nur, wenn de während de Rääs orre 20 Minudde vor de Live-Abfahrt (Plan-Abfahrt + Verspädung) incheckst. Wer 1 Stunn vor orre nooch em Rääsezeitraam incheckt, bekummt immerhi noch 1/4 vun de Pungde. Wenn de noch frieher orre späder in ä Verbinnung incheckst, bekommscht de die volle Kilomeder und Stunne gutgeschriwwe, awwer nur een Mitleidspungd.", "about.express": "InterCity, EuroCity", - "about.faq-heading": "Haifich gestellde Frooche", "about.feature-missing": "In der Version vun Träwelling fange mer emol ganz vun vorre an – dodebei kanns sinn, dass wenisch-verwendete Funktione noch ned ingebaud sinn. Wenn du en Feature vorschlaan willschd, kannsche ääfach a Mail schreibe an", "about.feature-missing-heading": "Do fehlt ebbes! Wissoo gebts des Feature nimmi?", - "about.heading": "Iwwer Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "De Name is e Anspielung uff des allseids bekannde \"Senk ju for träwelling wis Deutsche Bahn\", was mer eichentlich in faschd jedem Fernverkehrzuuch vun de Deutsche Bahn geheert han sollt.", - "about.name-heading": "Vun wo kummt de Name?", - "about.no-train": "Mir verwenne e Schniddstell vun de Deutsche Bahn, bei dere ned alle Angebode direkt debei sinn. Leider könne mir net viel dran mache, wenn dei Zuuch ned debei is.", - "about.no-train-heading": "Warum werd do mei Zuuch ned ufgelischd?", - "about.points-heading": "Wie werre die Pungde berechelt?", - "about.points1": "Die Pungde setze sich aus de Produktklass un de Entfernung vun deiner Rääs zamme.", - "about.productclass": "Produktklass", "about.regional": "Regionalbahn/-express", "about.suburban": "S-Bahn, Fähr", "about.tram": "Tram / Stadtbahn, Bus, U-Bahn", - "about.who": "An Träwelling werd seit 2013 vun immer annere Leit entwickelt. Dääls nur Bugfixes, dääls greeßere Ännerunge. Genaure Infos findscht du in de", - "about.who-heading": "Wer entwickelt Träwelling?", "admin.greeting": "Guun Daach", "admin.usage": "Nutzung", "admin.usage-board": "Nutzungsauswerdung", @@ -399,14 +386,6 @@ "menu.loading": "Lade", "messages.exception.already-checkedin": "'S gebbt schun ä Checkin in der Verbindung.", "messages.exception.maybe-too-fast": "Vielleicht hasche dei Anfrage ausversehe mehrfach abgeschickt?", - "login-required": "(Login benötigt)", - "about.events": "Was sinn Veranschdaldunge?", - "about.events.description1": "'s Träwelling bringd Mensche zamme, die zesamme öffendliche Verkehrsmiddel nutze. Manchesmol fahre se sogar zur selwe Veranschdaldung, ohne vunenanner zu wisse!", - "about.events.description2": "Deswege hawwe mir die Veranschdaldungs-Funktion gemach. Jede*s kann üwwer unser Formular ä Veranschdaldung fer en beschdimder Zeitraum anlegge und alle Benutzer*inne vun Träwelling känne ihr Check-Ins mit dere Veranschdaldung verknüpfe.", - "about.events.description4": "Veranschdaldunge sollde ebbes Besonneres sin. Deswege hann mir uns entschiede, kläne lokale Veranschdaldunge wie Weihnachtsmärkte unn Stadtfeschde prinzipiell anzulehne. Ausnahme beschdädische hier die Regel.", - "about.who0": "Träwelling is en Open-Source-Projekt.", - "about.who1": "Seit 2013 entwiggele unnerschiedlische Persone am Projekt. Teilweis nur Bugfixes, teilweis größere Ännerunge.", - "about.who2": "Uff GitHub kannsch du dir ä Liste vunn am Code Beteilischde angugge.", "user.email.new": "Naie E-Mail-Adress", "user.login-credentials": "E-Mail-Adress odder Nutzername", "user.profile-picture": "Profilbild vun @:username", @@ -422,9 +401,7 @@ "modals.setHome-title": "Heimatbahnhof setze", "user.email.change": "E-Mail-Adress ännere", "user.likes-enabled": "\"Gefällt mir\" anzeiche", - "about.events.description3": "Wenn du en Veranschdaldung anleche willsch, bass uff, dass die Veranschdaldung en Merhwert fer die ganz Community vun Träwelling hann sollt. Dodezu geheere Veranschdaldunge ausm Eisebahnwese wie bspw. die letscht Fahrt vum Metropolitan, Events vun Hackerspaces wie bspw. GPN, odder vun de LGBTQ-Szen' wie bspw. de Kölner CSD (Cologne Pride).", "error.bad-request": "Die Anfaach is ungüldich.", - "about.who3": "Zusätzlich hann ä paar Persone bei de Alpha-Version unnerstützt. Die finnsche hier.", "export.error.time": "Du kannsch nur Fahrte üwwer en Zeitraum vun maximal 365 Daa exportiere.", "export.error.amount": "Du hasch mehr als 2000 Fahrte angefroot. Bitte versuch de Zeitraum inzuschänke.", "menu.settings.followings": "Folg ich", diff --git a/lang/en.json b/lang/en.json index 18deb1a88..9483d7de1 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,36 +1,14 @@ { - "about.basepoints": "Base points", "about.block1": "Träwelling is a free check-in service that lets you tell your friends where you are and where you can log your public transit journeys. In short, you can check into trains and get points for it.", - "about.calculation": "The distance is rounded to the nearest 10km and then divided by 10. Afterwards the base points are added.
An ICE journey of 143km thus earns you 10 + round up(143/10) = 10 + 15 = 25 Points, for an S-Bahn journey of 8km you get 2 + round up(8/10) = 2 + 1 = 3 Points.
We want you to check in as soon as possible so that the platform always has an up-to-date status of your journeys. Therefore, you will only receive full points if you check in during your journey or 20min before live departure (scheduled departure + delay). If you check in 1h before or after the travel period, you can still get 1/4 of the points. If you check in earlier or later on a connection, you will receive the full kilometres and hours, but only one pity point.", "about.express": "InterCity, EuroCity", - "about.events": "What are events?", - "about.events.description1": "Träwelling brings people together who use the same public transport. Sometimes those people even ride to the same events, without knowing from another!", - "about.events.description2": "That’s why we’ve created the Events feature. Anyone can suggest an event in a specific time frame; and once accepted, all users can connect their check-ins to that event.", - "about.events.description3": "If you want to create an event, please be aware that events should benefit the entire Träwelling community. Those events can be from the European railway community such as the last course of a train model, hacking events such as GPN, or from the LGBTQ scene such as Cologne Pride.", - "about.events.description4": "Events are something special. That’s why we decided to deny suggestions to smaller, local events such as Christmas Markets or city fairs. As always, there are exceptions to that rule.", - "about.faq-heading": "Frequently asked questions", "about.feature-missing": "If you would like to suggest a feature or have found a bug, please report it directly to our GitHub repository.", "about.feature-missing-heading": "How do I report errors or suggestions for improvement?", - "about.heading": "About Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "The name is an allusion to the well-known \"Senk ju for träwelling wis Deutsche Bahn\", which you should have heard in almost every long-distance train of the Deutsche Bahn.", - "about.name-heading": "Where does the name come from?", - "about.no-train": "We use an interface of the Deutsche Bahn, where not all offers are displayed directly. Unfortunately, we can't do much if your train is not listed.", - "about.no-train-heading": "Why isn't my train listed?", - "about.points-heading": "How are points calculated?", - "about.points1": "The points consist of the product class and the distance of your journey.", - "about.productclass": "product category", "about.regional": "Regional train/-express", "about.suburban": "Suburban rail, Ferry", "about.tram": "Tram / light rail, bus, subway", - "about.who0": "Träwelling is an open source project.", - "about.who1": "Since 2013, various people have been developing the project. Partly only bugfixes, partly major changes.", - "about.who2": "On GitHub you can view a list of contributors to the code.", - "about.who3": "Additionally, a few people have assisted with the alpha version. You can find them here.", - "about.who-heading": "Who develops Träwelling?", "admin.greeting": "Hello", "admin.usage": "Usage", - "login-required": "(Login required)", "admin.usage-board": "Usage Board", "admin.select-range": "Select range", "admin.checkins": "Check-ins", @@ -67,6 +45,7 @@ "user.home-not-set": "You haven't set a home station yet.", "user.private-profile": "Private profile", "user.likes-enabled": "Show likes", + "user.points-enabled": "Show points and leaderboard", "user.liked-status": "likes this status.", "user.liked-own-status": "likes their own status.", "user.invalid-mastodon": ":domain doesn't seem to be a Mastodon instance.", @@ -488,6 +467,11 @@ "dateformat.month-and-year": "MMMM YYYY", "time-format.with-day": "hh:mm a (DD.MM.YYYY)", "dashboard.future": "Your future check-ins", + "dashboard.empty": "Your dashboard seems a bit empty.", + "dashboard.empty.teaser": "If you want to, you can follow some people to see their check-ins here.", + "dashboard.empty.discover1": "You can discover new people in the section", + "dashboard.empty.discover2": "or", + "dashboard.empty.discover3": "(careful, slow load times)", "user.block-tooltip": "Block user", "user.blocked": "You have blocked the user :username.", "user.already-blocked": "The user :username is already blocked.", @@ -527,15 +511,9 @@ "description.profile": ":username has already traveled :kmAmount kilometers in :hourAmount hours on public transportation.", "date-format": "YYYY-MM-DD", "transport_types.regionalExp": "Regional Express", - "support": "Support", - "support.create": "Create support request", - "support.submit": "Submit support request", - "support.email-required": "You need a verified email address to submit a support request. Only then we can answer you.", "go-to-settings": "Go to settings", "subject": "Subject", "how-can-we-help": "How can we help?", - "support.answer": "We will answer you as soon as possible. The answer will be sent to your email address :address.", - "support.success": "We have received your request (Ticket #:ticketNumber). We will answer you as soon as possible by e-mail.", "settings.visibility": "Visibility", "settings.visibility.disclaimer": "If your profile is private, even statuses marked as public will only be shown to your followers.", "settings.visibility.default": "Check-ins: Default visibility", @@ -563,6 +541,8 @@ "checkin.points.could-have": "You could have gotten more points if you had checked in closer to the real departure time!", "checkin.points.full": "You could have earned :points points.", "checkin.points.forced": "You did not receive any points for this check-in because you forced it.", + "checkin.success.body": "You've successfully checked in!", + "checkin.success.body2": "You're traveling :distance km in :lineName from :origin to :destination.", "checkin.success.title": "Checked in successfully!", "checkin.conflict": "A parallel check-in already exists.", "checkin.conflict.question": "Do you still want to check in? You will not earn any points for this, but your personal statistic will still be updated.", @@ -603,21 +583,7 @@ "overlapping-checkin.force-yes": "Yes, enforce.", "overlapping-checkin.force-no": "No, do nothing.", "report-bug": "Report bug", - "to-support": "Support request", "request-feature": "Request feature", - "about.missing-verification-email": "Why don't I receive a confirmation email?", - "about.missing-verification-email.description": "First of all, please check if the email is in the spam folder.", - "about.missing-verification-email.description2": "If the email is not in the spam folder, please add ':email' to your contacts and request the email again.", - "about.missing-verification-email.description3": "If the email still won't arrive, please contact us via our contact form.", - "about.missing-verification-email.description4": "Please note that using Microsoft addresses (e.g. Live, Outlook, Hotmail, ...) occasionally caused problems in the past .", - "about.missing-verification-email.description5": "In this case, please try an email with another provider first.", - "about.points-real-time": "If your connection is delayed you should make sure to check in before the real departure time, as we cannot get real-time information about the trip after the real departure time.", - "about.dev": "I would like to contribute to Träwelling!", - "about.dev.1": "Träwelling is an open source project and everyone can contribute.", - "about.dev.2": "You can find all the information about it on our GitHub page.", - "about.dev.3": "We are happy about every contribution - simply create a PullRequest!", - "about.dev.4": "You can either work on your own ideas or pick a task from our Issues on GitHub.", - "about.dev.5": "If you have any questions, feel free to contact us on our discord server: :link", "other": "Other", "exit": "Exit", "platform": "Platform", @@ -668,7 +634,6 @@ "scopes.write-followers": "accept follow requests and remove followers", "scopes.write-blocks": "block and unblock users, mute and unmute users", "scopes.write-event-suggestions": "suggest events in your name", - "scopes.write-support-tickets": "create support requests in your name", "scopes.read-settings": "see your settings, email, etc.", "scopes.write-settings-profile": "edit your profile", "scopes.read-settings-profile": "see your profile data, e.g. email", @@ -691,7 +656,7 @@ "events.disclaimer.warranty": "Träwelling does not guarantee the correctness or completeness of the data.", "user.mapprovider": "Provider of map data", "user.timezone": "Timezone", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "map-providers.open-railway-map": "Open Railway Map", "active-journeys": "active journey|active journeys", "page-only-available-in-language": "This page is only available in :language.", @@ -794,6 +759,9 @@ "trip_creation.limitations.3": "The trip is created public - so if you check in to a trip, everyone who can see your status can also check in to this trip.", "trip_creation.limitations.4": "The operator can't be set in this form (possible via API)", "trip_creation.limitations.5": "There are no visible error messages for this form. So, if nothing happens on submit... sorry. There is an error.", + "trip_creation.limitations.6": "Only the selectable types of vehicles are allowed (i.e. trips by car, bicycle, on foot, etc. are not permitted and may be deleted). See also:", + "trip_creation.limitations.6.rules": "Rules", + "trip_creation.limitations.6.link": "https://help.traewelling.de/en/features/manual-trips/#info", "action.error": "This action could not be executed. Please try again later.", "action.like": "Like status", "action.dislike": "Dislike status", diff --git a/lang/eo.json b/lang/eo.json index 32882a4ae..88155cf94 100644 --- a/lang/eo.json +++ b/lang/eo.json @@ -1,5 +1,4 @@ { - "about.basepoints": "Bazopunktoj", "admin.usage": "uzo", "admin.checkins": "Checkin-Inoj", "admin.greeting": "Saluton", diff --git a/lang/es.json b/lang/es.json index 16a4d8a13..061a84d86 100644 --- a/lang/es.json +++ b/lang/es.json @@ -1,36 +1,14 @@ { - "about.basepoints": "Puntos de base", "about.block1": "Träwelling es un servicio gratuito check-in que te deja mostrar a tus amigos y amigas donde estás y te deja anotar tus viajes de transporte público. Basicamente, podes hacer check-in a trenes y recibir puntos.", - "about.calculation": "La distancia es redondeada a los 10km mas cercanos y dividida por 10. Despues, los puntos de base son añadidos.
Así, un viaje en ICE de 143km resulta en 10 + round up(143/10) = 10 + 15 = 25 puntos, un viaje en S-Bahn de 8km resulta en 2 + round up(8/10) = 2 + 1 = 3 puntos.
Querémos que hagas tu check-in lo mas rápido posible para que la plataforma siempre tenga el estatús actual de tu viaje. Por lo tanto, solo vas a recibir los puntos totales si hacés tu check-in mientras o 20min antes de tu partida actual (partida programada + retrazo). Si hacés tu check-in 1h antes o despues de tu viaje, recibirás 1/4 de los puntos. Si hacés tu check-in fuera de este tiempo, recibiräs todos los kilómetros y todas las horas, pero solo un punto, de pena.", "about.express": "InterCity, EuroCity", - "about.events": "¿Qué son eventos?", - "about.events.description1": "Träwelling une los que usen el mismo transporte pública. ¡A veces, esta gente va a los mismos eventos, sin saber del otro!", - "about.events.description2": "Por eso hemos creado la funcionalidad Eventos. Cualquiera puede sugerir un evento en un periodo de tiempo específico; y si aceptado, todos los usuarios queden conectar su check-in a este evento.", - "about.events.description3": "Si quieres crear un evento, debes ser seguro/a que estos eventos beneficien la comunidad entera de Träwelling. Pueden ser eventos de la comunidad ferroviaria europea, como el último curso del modelo de tren Metropolitan, eventos de la escena hacker como la GPN o eventos de la escena LGBTQ como la marcha de orgullo de Colonia.", - "about.events.description4": "Eventos son algo especial. Por eso decidimos no aceptar sugerencias a eventos pequeños y locales como mercados de navidad o ferias. Como siempre, hay excepciones.", - "about.faq-heading": "Preguntas frequentes", "about.feature-missing": "Si quieres sugerir alguna funcionalidad o encontraste un error, por favor informanos directamente en nuestro repositorio GitHub.", "about.feature-missing-heading": "¿Como puedo reportar errores o sugerir funcionalidades?", - "about.heading": "Sobre Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "El nombre es una referencia a la frase conocida \"Senk ju for träwelling wis Deutsche Bahn\", que debes haber escuchado si has tomado cualquier tren de larga distancia alemán.", - "about.name-heading": "¿De dónde sale el nombre?", - "about.no-train": "Utilizamos un interfaz de Deutsche Bahn, que no muestra todas los servicios. Por esa razón, el número de trenes disponibles en España es limitado, y no hay autobuses o metros. Desafortunadamente, si no está tu tren, no podemos hacer mucho.", - "about.no-train-heading": "¿Porque no está mi tren?", - "about.points-heading": "¿Cómo se calculan los puntos?", - "about.points1": "Los puntos consisten de la clase de producto y la distancia del viaje.", - "about.productclass": "Clase de produto", "about.regional": "Tren regional (RB, RE)", "about.suburban": "S-Bahn, Transbordador", "about.tram": "Tranvía, Autobús, Metro", - "about.who0": "Träwelling es un proyecto de fuente abierta.", - "about.who1": "Desde 2013, varias personas han participado en el desarrollo del proyecto. Parcialmente en sacar errores, parcialmente en cambios mayores.", - "about.who2": "En GitHub puedes ver una lista de contribuyentes del código.", - "about.who3": "Además, algunas personas han ayudado con la version alpha. Las puedes encontrar aquí.", - "about.who-heading": "¿Quién hizo Träwelling?", "admin.greeting": "Hola", "admin.usage": "Uso", - "login-required": "(Login obligatorio)", "admin.usage-board": "Tablero de uso", "admin.select-range": "Seleccionar rango", "admin.checkins": "Check-ins", diff --git a/lang/fr.json b/lang/fr.json index ad055000c..50892dc64 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -1,5 +1,4 @@ { - "about.faq-heading": "Questions fréquentes", "controller.status.like-already": "Tu as déjà aimé cet enregistrement.", "controller.transport.checkin-heading": "Enregistré", "controller.status.export-neither-business": "Tu ne peux pas abandonner à la fois les voyages privés et les voyages d’affaires.", @@ -74,8 +73,6 @@ "profile.private-profile-text": "Ce profil est privé.", "user.displayname": "Nom d’affichage", "events.on-my-way-dropdown": "Je suis en route pour :", - "about.name-heading": "D’où vient le nom ?", - "about.basepoints": "Point de base", "messages.cookie-notice": "Nous utilisons des cookies pour notre système de connexion.", "profile.settings": "Réglages", "user.complete-registration": "Terminer l’inscription", @@ -83,7 +80,6 @@ "events.upcoming": "Évènements à venir", "controller.status.create-success": "Enregistrement créé.", "settings.btn-update": "Mettre à jour", - "about.points1": "Les points sont calculés en respectant la classe de produit et la distance du trajet.", "events.new": "Créer un évènement", "auth.throttle": "Trop de tentatives de connexion. Réessaye dans :seconds secondes.", "settings.expires": "Expire", @@ -92,7 +88,6 @@ "settings.delete-account": "Supprimer le compte", "transport_types.nationalExpress": "Grandes lignes (TGV, ICE, …)", "settings.picture": "Photo de profil", - "about.no-train": "Nous utilisons une interface de la Deutsche Bahn, c’est pourquoi il y a de temps en temps des trajets qui manquent. Malheureusement, nous n’avons pas la possibilité d’ajouter manuellement ton train quand il manque.", "user.invalid-mastodon": ":domain ne semble pas être une instance Mastodon.", "menu.gohome": "Page d’accueil", "export.begin": "À partir de", @@ -110,7 +105,6 @@ "about.feature-missing": "Si vous souhaitez suggérer une fonctionnalité ou trouver un bug, veuillez le signaler directement sur notre GitHub.", "controller.social.create-error": "Il y a eu un problème lors de la création de ton compte.", "stationboard.plus-15": "+15 minutes", - "about.heading": "À propos de Träwelling", "user.not-received-link": "clique ici pour en envoyer un nouveau", "stationboard.label-message": "Message d’enregistrement :", "controller.user.follow-delete-not-permitted": "L’action n’est pas autorisée.", @@ -139,7 +133,6 @@ "stationboard.dt-picker": "Sélecteur de date", "dates.October": "octobre", "menu.abort": "Abandonner", - "about.name": "Le nom est une allusion au célèbre « Senk ju for träwelling wis Deutsche Bahn » ( Merci de voyager avec la Deutsche Bahn) que vous pouvez entendre dans presque tous les trains grandes lignes de la Deutsche Bahn.", "settings.notconnected": "Pas connecté(e)", "menu.developed": "Codé avec dans l’Union européenne. Code source publié sous la licence AGPLv3.", "user.email-verify": "Confirmer l’adresse e-mail", @@ -176,13 +169,11 @@ "leaderboard.duration": "Temps de parcours", "events.past": "Évènements passés", "controller.transport.no-name-given": "Il faut saisir le nom d’une station !", - "about.who-heading": "Qui développe Träwelling ?", "menu.login": "Se connecter", "controller.user.password-changed-ok": "Mot de passe modifié.", "stationboard.last-stations": "Dernières stations", "settings.confirm-password": "Confirmer le mot de passe", "stationboard.btn-checkin": "S’enregistrer !", - "about.no-train-heading": "Pourquoi mon train n’est pas dans la liste ?", "transport_types.subway": "métro", "menu.settings": "Réglages", "events.closestStation": "Station Träwelling la plus proche", @@ -197,8 +188,6 @@ "export.btn": "Exporter les donnés", "settings.ip": "Adresse IP", "dates.Sunday": "dimanche", - "about.who": "Träwelling est conçu et développé depuis 2013 par toujours plus de gens. En partie pour des correctifs, en partie pour de grosses modifications. Tu trouveras plus d’infos dans", - "about.productclass": "Classe de produit", "notifications.userJoinedConnection.notice": "@:username voyage dans :linename de :origin à :destination.|@:username voyage dans :linename de :origin à :destination.", "dates.December": "décembre", "export.lead": "Ici, tu peux exporter tes trajets depuis la base de données au format CSV, JSON et PDF.", @@ -252,14 +241,12 @@ "dates.thousands_sep": " ", "about.international": "TGV, InterCityExpress, RailJet", "menu.discard": "Rejeter", - "about.calculation": "La distance est arrondie à la dizaine suivante ensuite divisée par 10 puis les points de base sont ajoutés.
Un trajet en TGV de 143 km te donne 10 + arrondi(143/10) = 10 + 15 = 25 points, pour un trajet en RER de 8 km tu vas recevoir 2 + arrondi(8/10) = 2 + 1 = 3 points.
Nous voulons que tu publies ton enregistrement dans les meilleur délais, c’est pourquoi tu ne reçois tous les points que pour un enregistrement étant publié pendant ou 20 minutes avant le départ réel. Pour un enregistrement une heure après le voyage, tu reçois seulement un quart des points. Si tu publies l’enregistrement plus tard ou plus tôt, tu reçois seulement un point de consolation.", "leaderboard.rank": "Rang", "stationboard.minus-15": "-15 minutes", "settings.upload-image": "Télécharger une photo de profil", "settings.create-ics-token-success": "Le partage de calendrier a été créé. Tu peux inclure le lien suivant dans un calendrier prenant en charge le format ICS : :link", "controller.transport.checkin-ok": "Tu as publié un enregistrement dans :lineName avec succès !|Tu as publié un enregistrement dans :lineName avec succès !", "controller.social.delete-set-password": "Avant de supprimer un fournisseur SSO, tu dois définir un mot de passe pour ne pas perdre ton accès.", - "about.points-heading": "Comment sont calculés les points ?", "settings.client-name": "Service", "controller.user.password-wrong": "L’ancien mot de passe est incorrect.", "user.not-received-before": "Si tu n’a pas reçu le courriel ", @@ -470,17 +457,12 @@ "settings.follower.no-followings": "Tu ne suis personne.", "generic.change": "Changer", "how-can-we-help": "Comment est-ce qu’on peut t‘aider ?", - "support": "Support", - "support.create": "Créer une demande de support", - "support.submit": "Envoyer une demande de support", - "support.email-required": "Il te faut avoir une adresse e-mail vérifiée pour envoyer une demande de support. Sinon on ne peut pas te reprendre.", "settings.follower.no-requests": "Tu n‘as aucune demande de suivre.", "generic.why": "Pourquoi ?", "transport_types.taxi": "Taxi", "settings.ics.modal": "Jetons ICS délivré", "settings.revoke-token.success": "L'accès est révoqué", "settings.ics.descriptor": "Ici tu peux gérer ton lien ICS. C'est pour montrer tes trajets précédents dans un calendrier qui le soutient.", - "support.answer": "Notre équipe va te répondre dès que possible. La réponse sera envoyé à l'adresse mail suivante :address.", "support.privacy": "Déclaration de confidentialité", "support.privacy.description": "Ton numéro d'identification, ton nom d'utilisateur et ton adresse mail sont enregistrés ensemble avec ta demande dans notre système Ticket.", "settings.follower.following-since": "Suit depuis", @@ -490,7 +472,6 @@ "request-time": "demandé à :time", "checkin.points.could-have": "Vous auriez pu obtenir plus de points si vous vous étiez enregistré plus près de l'heure de départ réelle !", "checkin.points.forced": "Tu n'as pas reçu aucun point pour cet enregistrement car tu l'as obligé.", - "support.success": "On a reçu ta demande (Ticket #:ticketNumber). On va te répondre par courriel dès que possible.", "support.privacy.description2": "Les données seront supprimées indépendamment de ton compte Träwelling après un an.", "christmas-mode.disable": "Désactiver le mode Noël pour cette séance", "merry-christmas": "On te souhaite joyeux Noël !", @@ -521,11 +502,6 @@ "email.verification.too-many-requests": "Veuillez patienter quelques minutes avant de demander un autre mail de confirmation.", "overlapping-checkin": "Chevauchement d'enregistrement", "no-points-message.forced": "Vous n'avez pas obtenu tous les points pour avoir forcé l'enregistrement.", - "about.who0": "Träwelling est un projet Open-Sources.", - "about.who1": "Depuis 2013 diverses personnes ont développé le projet. Quelques corrections de bugs et aussi quelques changements plus importants.", - "about.who2": "Vous pouvez voir la liste des contributeurs au code sur GitHub.", - "about.who3": "De plus, quelques personnes ont aidé avec la version alpha. Vous pouvez les trouver ici.", - "login-required": "(Connexion requise)", "overlapping-checkin.description": "Votre voyage n'a pas pu être enregistré car il chevauche votre voyage dans le :lineName.", "overlapping-checkin.description2": "Souhaitez-vous forcer l'enregistrement maintenant ?", "export.error.amount": "Vous avez demandé plus de 2000 trajets. Veuillez ré-essayer en limitant la période.", @@ -537,24 +513,8 @@ "experimental-features": "Fonction expérimentale", "overlapping-checkin.force-yes": "Forcer.", "report-bug": "Signaler une erreur", - "to-support": "Demande au support", "export.error.time": "Vous ne pouvez exporter des voyages que pour une période maximale de 365 jours.", "notifications.eventSuggestionProcessed.lead": "Votre suggestion d'événement :nom a été modifiée.", - "about.missing-verification-email.description2": "Si l'e-mail n'est pas dans le dossier spam, veuillez ajouter ':email' à vos contacts et demander l’envoie d’un nouveau e-mail de confirmation.", - "about.dev": "J'aimerais aussi contribuer au développement de Träwelling !", - "about.dev.1": "Träwelling est un projet open source et tout le monde peut y contribuer.", - "about.missing-verification-email.description3": "Si l'e-mail n'arrive toujours pas, veuillez nous contacter via notre formulaire de contact.", - "about.missing-verification-email.description4": "Veuillez noter que l'utilisation d'adresses Microsoft (par exemple Live, Outlook, Hotmail, ...) a parfois causé des problèmes dans le passé.", - "about.missing-verification-email.description": "Tout d'abord, veuillez vérifier si l'e-mail se trouve dans le dossier spam.", - "about.events": "Que sont les événements ?", - "about.events.description1": "Träwelling rassemble les personnes qui utilisent les transports publics. Parfois, ils vont même aux mêmes événements sans se connaître !", - "about.events.description4": "Les événements doivent être quelque chose de spécial. C'est pourquoi nous avons décidé de rejeter par principe les petits événements locaux tels que les marchés de Noël et les fêtes de villages. Les exceptions confirment la règle.", - "about.missing-verification-email": "Pourquoi est-ce que je ne reçois pas d'e-mail de confirmation ?", - "about.missing-verification-email.description5": "Dans ce cas, essayez avec un e-mail d'un autre fournisseur.", - "about.points-real-time": "Si votre correspondance est retardée, vous devez vous assurer de vous enregistrer avant l'heure de départ réelle, car nous ne pouvons plus obtenir d'informations en temps réel sur le voyage après le départ effectif.", - "about.dev.2": "Vous trouverez toutes les informations à ce sujet sur notre page GitHub.", - "about.dev.3": "Nous attendons avec impatience chaque contribution - créez simplement une pull request !", - "about.dev.5": "Si vous avez des questions, n'hésitez pas à nous contacter sur notre serveur Discord : :link", "other": "Autre", "exit": "Sortir", "platform": "Voie", @@ -567,10 +527,7 @@ "warning.insecure-performance": "Le temps de chargement de cette page peut être très lent s'il y a beaucoup de données.", "year-review": "Revue de votre année", "year-review.open": "Voir la revue de l’année", - "about.events.description2": "C'est pourquoi nous avons intégré la fonction événements. N'importe qui peut créer un événement pour une période de temps spécifique en utilisant le formulaire et tous les utilisateurs de Träwelling peuvent alors lier leurs check-ins à l'événement.", - "about.dev.4": "Vous pouvez soit travailler sur vos propres idées, soit choisir une tâche parmi nos problèmes sur GitHub.", "support.go-to-github": "Veuillez nous signaler les bugs et les suggestions d'amélioration sur GitHub. Utilisez les boutons suivants pour cela. Vous pouvez utiliser ce formulaire pour que nous vous aidions si vous rencontrez des problèmes avec votre compte ou vos enregistrements sur traewelling.de.", - "about.events.description3": "Si vous souhaitez créer un événement, veuillez noter que les événements doivent avoir une valeur ajoutée pour l'ensemble de la communauté Träwelling. Il s'agit notamment d'événements ferroviaires tels queLe dernier voyage du métropolitain, d'événements provenant de hackerspaces (par exemple, leGPN), ou de la scène LGBTQ comme la Pride de Cologne.", "no-journeys-day": "Vous n'avez enregistré aucun trajet ce jour-là.", "stats.daily.description": "Journal de voyage avec carte", "year-review.teaser": "L'année s'achève et nous avons vécu beaucoup de choses ensemble. Jetez un coup d'œil à votre bilan de fin d'année Träwelling :", @@ -622,7 +579,6 @@ "scopes.write-follows": "suivre et ne plus suivre les utilisateurs en votre nom", "scopes.write-followers": "accepter les demandes de suivi et supprimer des abonnés", "scopes.write-event-suggestions": "suggérer des événements en votre nom", - "scopes.write-support-tickets": "créer des demandes d'assistance en votre nom", "scopes.read-settings": "voir vos paramètres, e-mail, etc...", "scopes.write-settings-profile": "éditer votre profil", "scopes.read-settings-profile": "voir vos données de profil, par ex. e-mail", @@ -651,7 +607,7 @@ "settings.webhook-event-notifications-description": "Activités", "settings.webhook_event.checkin_create": "Création d'un check-in", "settings.webhook_event.notification": "Recevoir une notification", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "notifications.eventSuggestionProcessed.too-late": "Malheureusement, votre proposition a été soumise trop tard.", "notifications.eventSuggestionProcessed.not-applicable": "Votre suggestion n'apporte malheureusement aucune valeur ajoutée à la communauté.", "settings.twitter-deprecated": "Lire la note de dépréciation", diff --git a/lang/it.json b/lang/it.json index 00b491087..bf124a5c5 100644 --- a/lang/it.json +++ b/lang/it.json @@ -1,19 +1,11 @@ { "about.express": "Intercity, Eurocity", - "about.faq-heading": "Domande frequenti", "about.feature-missing": "Se vuoi suggerire una funzionalità o hai trovato un bug, puoi segnalarlo direttamente sul nostro repository GitHub.", "about.feature-missing-heading": "Come posso segnalare errori o fare delle proposte di miglioramento?", - "about.heading": "Che cos'è Träwelling?", "about.international": "Intercity/Eurocity Express, TGV, Railjet", - "about.name-heading": "Qual è l'origine del nome?", - "about.no-train": "Usiamo un'interfaccia delle ferrovie tedesche (Deutsche Bahn) che non visualizza direttamente tutte le soluzioni. Purtroppo non c'è molto che possiamo fare se il tuo treno non è incluso.", - "about.no-train-heading": "Perché il mio treno non è presente nell'elenco?", - "about.points-heading": "Come vengono calcolati i punti?", - "about.productclass": "Categoria di servizio", "about.regional": "Servizio regionale (R, RV/RGV, RE)", "about.suburban": "Treno suburbano, traghetto", "about.tram": "Tram/tranvia, autobus, metropolitana", - "about.who-heading": "Chi si occupa dello sviluppo di Träwelling?", "admin.greeting": "Ciao", "admin.usage": "L'uso", "admin.usage-board": "Analisi dell'uso", @@ -42,16 +34,11 @@ "controller.status.like-not-found": "Like non trovato.", "controller.status.like-ok": "Like effettuato!", "controller.status.not-permitted": "Non ti è permesso di fare questo.", - "about.basepoints": "Punti base", "about.block1": "Träwelling è un servizio gratuito di check-in che ti permette di dire ai tuoi amici dove stai viaggiando con i trasporti pubblici e di tenere un diario dei tuoi viaggi. In breve, puoi effettuare check-in sui treni e raccogliere punti per averlo fatto.", - "about.who": "Träwelling è stato sviluppato da diverse persone dal 2013. In parte solo correzioni di bug o anche cambiamenti importanti. Potete trovare informazioni più dettagliate nella sezione", "admin.hafas-entries-by-type": "Voci HAFAS secondo il tipo di trasporto", - "about.calculation": "La distanza viene arrotondata ai prossimi 10 km e poi divisa per 10. Successivamente vengono aggiunti i punti base.
Un viaggio in un treno di lunga percorrenza o Alta Velocità (Frecce, ICE) di 143 km ti fa guadagnare 10 + arrotondare(143/10) = 10 + 15 = 25 punti. Un viaggio in S-Bahn (treno suburbano o metropolitano) di 8 km ti fa guadagnare 2 + arrotondare(8/10) = 2 + 1 = 3 punti.
Vogliamo che tu faccia il check-in il prima possibile in modo che la piattaforma abbia sempre informazioni aggiornate sui tuoi viaggi. Quindi riceverai il punteggio completo solo se farai il check-in durante il tuo viaggio o 20 minuti prima della tua partenza effettiva (partenza prevista + ritardo). Se fai il check-in 1 ora prima della partenza o 1 ora dopo dell'arrivo, ricevi comunque ancora 1/4 dei punti. Se fai il check-in più di 1 ora prima della partenza o più di 1 ora dopo l'arrivo, riceverai i chilometri e le ore complete, ma solo un punto di pietà.", "admin.hafas-entries-by-polylines": "Voci HAFAS e numero di polylines associate", "auth.throttle": "Troppi tentativi di accesso. Riprova tra :seconds secondi.", - "about.name": "Il nome è un'allusione alla nota frase \"Senk ju for träwelling wis Deutsche Bahn\", che dovresti aver sentito su quasi tutti i treni a lunga percorrenza delle ferrovie tedesche (Deutsche Bahn).", "controller.social.delete-set-email": "Prima di eliminare un provider SSO, devi impostare un indirizzo e-mail in modo da non essere escluso.", - "about.points1": "I punti sono composti dalla classe del prodotto e dalla distanza del tuo viaggio.", "controller.social.delete-set-password": "Prima di eliminare un provider SSO, devi impostare una password in modo da non essere escluso.", "email.change": "Per favore, aggiorna la tua e-mail inserendo il nuovo indirizzo e la tua password attuale. Un'e-mail di conferma ti sarà inviata per confermare questo cambiamento.", "email.verification.sent": "Per completare il cambiamento del tuo indirizzo e-mail devi ancora cliccare sul link nell'e-mail che ti abbiamo appena inviato.", @@ -438,13 +425,9 @@ "description.en-route": "Mappa panoramica di tutti gli utenti Träwelling che sono attualmente in movimento nel mondo.", "leaderboard.notice": "I dati qui riportati si basano sui check-in degli ultimi 7 giorni. Aggiornamenti possono richiedere fino a cinque minuti.", "support": "Assistenza", - "support.create": "Creare una richiesta di assistenza", - "support.submit": "Inviare una richiesta di assistenza", "go-to-settings": "Andare alle impostazioni", "subject": "Soggetto", "how-can-we-help": "Come possiamo aiutarti?", - "support.answer": "Ti risponderemo il più presto possibile. La risposta sarà inviata al tuo indirizzo e-mail :address.", - "support.success": "Abbiamo ricevuto la tua richiesta (Ticket #:ticketNumber). Ti risponderemo il più presto possibile via e-mail.", "request-time": "richiesto alle :time", "settings.request.accept": "Accettare la richiesta di follow", "settings.follower.following-since": "Follower dal", @@ -491,7 +474,6 @@ "settings.title-loginservices": "Servizi connessi", "localisation.not-available": "Siamo spiacenti, questo contenuto non è attualmente disponibile nella tua lingua.", "settings.title-privacy": "Privacy", - "support.email-required": "Hai bisogno di un indirizzo e-mail verificato per inviare una richiesta di assistenza. Solo così potremo risponderti.", "settings.upload-image": "Caricare l'immagine del profilo", "settings.hide-search-engines": "Indicizzazione dei siti di ricerca", "settings.search-engines.description": "Abbiamo impostato un tag noindex sul tuo profilo per dire ai siti di ricerca che il tuo profilo non deve essere indicizzato. Non abbiamo alcuna influenza sul fatto se il sito di ricerca prende in considerazione questo desiderio.", @@ -520,33 +502,16 @@ "carriage": "Carrozza", "carriage-sequence": "Ordine delle carrozze", "experimental-feature": "Funzione sperimentale", - "about.dev.1": "Träwelling è un progetto open source e tutti possono contribuire.", - "about.dev.3": "Siamo felici di ogni contributo - basta creare una PullRequest!", "notifications.eventSuggestionProcessed.lead": "Il tuo suggerimento per l'evento :name è stato verificato.", "notifications.eventSuggestionProcessed.denied": "La tua proposta è stata rifiutata.", "settings.delete-account.more": "Rifiutare ed eliminare l'account", "stationboard.check-chainPost": "Allegare all'ultimo check-in inviato", - "about.events.description2": "Ecco perché abbiamo aggiunto la funzione eventi. Chiunque può creare un evento per un certo periodo di tempo tramite il nostro modulo e tutti gli utenti di Träwelling potranno poi collegare i loro check-in all'evento.", - "about.missing-verification-email.description3": "Se l'e-mail non arriva, contattaci tramite il nostro modulo di contatto.", - "about.events.description3": "Se vuoi creare un evento, assicurati che gli eventi siano di beneficio per l'intera comunità di Träwelling. Tali eventi comprendono eventi della comunità ferroviaria europea, come l'ultima corsa di un treno particolare, eventi di hacking come il GPN o eventi della scena LGBTQ come il Cologne Pride.", - "about.missing-verification-email.description4": "Attenzione: in passato si sono verificati spesso problemi con gli indirizzi Microsoft (ad es. Live, Outlook, Hotmail, ...).", - "about.points-real-time": "Se il tuo collegamento è in ritardo, assicurati di effettuare il check-in prima dell'orario di partenza effettivo, perché non possiamo ottenere informazioni in tempo reale sul viaggio dopo l'orario di partenza effettivo.", - "about.dev.4": "Puoi lavorare sulle tue idee o scegliere un compito dai nostri Issues su GitHub.", - "about.events.description1": "Träwelling unisce le persone che utilizzano il trasporto pubblico. A volte viaggiano addirittura allo stesso evento senza sapere nulla l'uno dell'altro!", "exit": "Uscire", "warning.insecure-performance": "Il tempo di caricamento di questa pagina può essere molto lento in presenza di una grande quantità di dati.", "overlapping-checkin": "Sovrapposizione di check-in", "overlapping-checkin.description": "Non è stato possibile salvare il viaggio perché si è sovrapposto al check-in nel :lineName.", - "about.who0": "Träwelling è un progetto open source.", - "about.who1": "Diverse persone stanno sviluppando il progetto dal 2013. In parte solo correzioni di bug, in parte modifiche importanti.", - "about.who2": "Su GitHub è possibile visualizzare l'elenco dei collaboratori al codice.", - "about.who3": "Inoltre, alcune persone hanno contribuito alla realizzazione della versione alfa. È possibile trovarle qui.", - "login-required": "(Login necessario)", "data-may-incomplete": "Le informazioni potrebbero essere incomplete o non corrette.", - "to-support": "Assistenza", "request-feature": "Richiesta di funzionalità", - "about.missing-verification-email": "Perché non ricevo un'e-mail di conferma?", - "about.missing-verification-email.description5": "In questo caso, prova prima l'e-mail di un altro provider.", "stats.stations": "Mappa della stazione", "warning-alternative-station": "Stai per effettuare il check-in per una partenza dalla stazione :newStation, che ti è stata mostrata perché è vicina a :searchedStation.", "profile.no-visible-statuses": "I viaggi di :username non sono visibili.", @@ -566,13 +531,6 @@ "no-points-message.forced": "Non hai ottenuto il massimo dei punti perché hai forzato il check-in.", "overlapping-checkin.force-yes": "Si, forzare.", "report-bug": "Segnalare un bug", - "about.events": "Cosa sono gli eventi?", - "about.events.description4": "Gli eventi devono essere qualcosa di speciale. Per questo motivo abbiamo deciso di rifiutare per principio i piccoli eventi locali come i mercatini di Natale e le feste urbane. Le eccezioni confermano la regola.", - "about.missing-verification-email.description": "Prima di tutto, controlla se l'e-mail si trova nella cartella spam.", - "about.missing-verification-email.description2": "Se l'e-mail non si trova nella cartella spam, aggiungi ':email' ai tuoi contatti e richiedi nuovamente l'e-mail.", - "about.dev": "Vorrei contribuire a Träwelling!", - "about.dev.2": "Tutte le informazioni sono disponibili sulla nostra pagina GitHub.", - "about.dev.5": "Se hai domande, non esitare a contattarci sul nostro server discord: :link", "platform": "Binario", "real-time-last-refreshed": "Ultimo aggiornamento dei dati in tempo reale", "help": "Aiuto", @@ -650,7 +608,6 @@ "scopes.write-exports": "creare esportazioni dai tuoi dati", "scopes.write-followers": "accettare le richieste di follow e rimuovere i follower", "scopes.write-event-suggestions": "suggerire eventi a tuo nome", - "scopes.write-support-tickets": "creare richieste di assistenza a tuo nome", "menu.oauth_authorize.third_party": "Questa applicazione non è un'applicazione ufficiale di Träwelling!", "menu.oauth_authorize.third_party.more": "Che cosa significa questo?", "scopes.read-statuses": "vedi tutti i tuoi stati", @@ -658,7 +615,7 @@ "scopes.read-statistics": "vedi le tue statistiche", "menu.oauth_authorize.scopes_title": "Questa applicazione richiede le seguenti autorizzazioni:", "user.mapprovider": "Fornitore di dati cartografici", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "events.disclaimer.warranty": "Träwelling non garantisce la correttezza o la completezza dei dati.", "page-only-available-in-language": "Questa pagina è disponibile solo in :language.", "language.en": "Inglese", diff --git a/lang/nb_NO.json b/lang/nb_NO.json index d5c06cd72..16c37932b 100644 --- a/lang/nb_NO.json +++ b/lang/nb_NO.json @@ -1,6 +1,5 @@ { "about.feature-missing": "Hvis du vil foreslå en funksjon eller fant en feil, vennligst rapporter den direkte til GitHub-depotet vårt< /a>.", - "about.productclass": "produktkategori", "about.suburban": "S-Bahn, ferge", "admin.greeting": "Hei", "admin.usage": "Bruk", @@ -191,13 +190,10 @@ "user.complete-registration": "Fullfør registrering", "user.email-verify": "Bekreft din e-postadresse", "user.forgot-password": "Glemt passordet?", - "about.calculation": "Distansen rundes til nærmeste 10km, og deles så på 10. Etterpå legges grunnpoengene til.
En ICE-reise på 143km gir deg dermed10 + opprunding (143/10) = 10 + 15 =25  poeng. For en S-Bahn-reise på 8km får du 2 + opprunding (8/10) = 2 + 1 = 3  poeng.
Vi ønsker at du sjekker inn så snart som mulig slik at plattformen alltid har en oppdatert status for reisene dine. Derfor vil du kun motta full poengsum hvis du sjekker inn i løpet av reisen din, eller 20 min før faktisk avgang (planlagt avgang+forsinkelse). Hvis du sjekker inn 1 time før eller etter reiseperioden kan du fremdeles få ¼ av poengene. Hvis du sjekker inn tidligere eller senere på en overgang, vil du motta alle kilometerne og timene, men kun ett trøstepoeng.", "user.fresh-link": "En ny bekreftelseslenke har blitt sendt til din e-postadresse.", "about.feature-missing-heading": "Hvordan rapporterer jeg feil eller forslag til forbedringer?", "user.mastodon-instance-url": "Instans", - "about.no-train-heading": "Hvorfor er ikke mitt tog opplistet?", "user.register": "Registrer", - "about.who-heading": "Hvem utvikler Trävelling?", "user.remember-me": "Husk meg", "admin.hafas-entries-by-polylines": "HAFAS-oppføringer og antall tilhørende polyliner", "user.reset-pw-link": "Send passordtilbakestillingslenke", @@ -224,12 +220,6 @@ "auth.required": "Du må være innlogget for å bruke denne funksjonen.", "dates.September": "September", "dates.Sunday": "Søndag", - "about.basepoints": "Basispoeng", - "about.faq-heading": "Ofte stilte spørsmål", - "about.heading": "Om Träwelling", - "about.name-heading": "Hvor kommer navnet fra?", - "about.points-heading": "Hvordan beregnes poeng?", - "about.points1": "Poengene består av produktklassen og avstanden for din reise.", "admin.registered-users": "Registrerte brukere", "admin.new-users": "nye brukere", "auth.failed": "Denne kontoen samsvarer ikke med våre arkiver.", @@ -297,9 +287,6 @@ "export.lead": "Her kan du eksportere togreiser fra databasen som CSV, JSON og PDF.", "about.express": "InterCity, EuroCity", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "Navnet er en hentydning til det velkjente \"Senk ju for träwelling wis Deutsche Bahn\", som egentlig burde vært hørt på nesten hvert langdistansetog som ble operert av Deutsche Bahn.", - "about.no-train": "Vi bruker et grensesnitt fra Deutsche Bahn, der ikke alle tilbud vises direkte. Dessverre er det ikke mye vi kan gjøre med det hvis toget ditt ikke er der.", - "about.who": "Träwelling har blitt utviklet av forskjellige mennesker siden 2013. Dels bare feilrettinger, dels store endringer. Du finner mer informasjon i", "controller.social.delete-set-password": "Før du sletter en SSO-leverandør, må du angi et passord for ikke å låse deg selv ute.", "controller.transport.also-in-connection": "I denne forbindelse er også:", "controller.transport.social-post": "Jeg er for tiden inne i :lineName to :Destination! #NowTräwelling | Jeg er i linje :lineName etter :Destination! #NowTräwelling ", @@ -455,11 +442,6 @@ "settings.ics.modal": "utstedt ICS-tokens", "settings.revoke-token.success": "Tilgangen er tilbakekalt", "generic.change": "Endring", - "support.create": "Opprett en støtteforespørsel", - "support.submit": "Send inn støtteforespørsel", - "support.email-required": "Du trenger en bekreftet e-postadresse for å sende inn en støtteforespørsel. Dette er den eneste måten vi kan svare deg på.", - "support.success": "Vi har mottatt forespørselen din (Billettnummer #:ticketNumber). Vi vil svare deg på e-post så snart som mulig.", - "support.privacy": "Informasjon om databeskyttelse", "email.change": "Vennligst oppdater e-posten din ved å skrive inn den nye adressen og ditt nåværende passord. Du vil da få tilsendt en bekreftelses-e-post som du kan bekrefte denne endringen med.", "user.email.new": "Ny e-postadresse", "user.email.not-set": "Ingen e-postadresse er lagt inn ennå", @@ -476,7 +458,6 @@ "support": "Brukerstøtte", "go-to-settings": "Til innstillingene", "how-can-we-help": "Hvordan kan vi hjelpe?", - "support.answer": "Vi vil svare deg så snart som mulig. Du får svar på din e-postadresse :address.", "support.privacy.description": "Din bruker-ID, brukernavn og e-postadresse vil bli registrert i vårt billettsystem med forespørselen din.", "support.privacy.description2": "Dataene vil bli slettet etter ett år uavhengig av brukerkontoen din hos Träwelling.", "menu.share": "dele", @@ -521,11 +502,6 @@ "overlapping-checkin": "Overlappende tur", "overlapping-checkin.description": "Reisen din kunne ikke lagres fordi den overlapper med innsjekkingen din i :lineName.", "overlapping-checkin.force-no": "Nei, ikke gjør noe.", - "about.who0": "Träwelling er et åpen kildekode-prosjekt.", - "about.who1": "Ulike personer har utviklet prosjektet siden 2013. Noen bare feilrettinger, noen større endringer.", - "about.who2": "Du kan se en liste over kodebidragsytere på
GitHub.", - "about.who3": "I tillegg har noen få hjulpet til med alfaversjonen. Du finner dem her.", - "login-required": "(innlogging kreves)", "status.visibility.4": "Kun registrerte brukere", "status.visibility.4.detail": "Kun synlig for registrerte brukere", "stats.time": "Din daglige reisetid", @@ -535,14 +511,7 @@ "data-may-incomplete": "Informasjonen kan være ufullstendig eller feil.", "empty-en-route": "Det er for tiden ingen Träwellers på veien.", "report-bug": "Rapporter en feil", - "to-support": "Gå til support", "request-feature": "Ønsker funksjon", - "about.missing-verification-email": "Hvorfor mottar jeg ikke en bekreftelses-e-post?", - "about.missing-verification-email.description": "Først av alt, vennligst sjekk om e-posten ligger i spam-mappen.", - "about.missing-verification-email.description2": "Hvis e-posten ikke er i spam-mappen, vennligst legg til ':email' i kontaktene dine og be om e-posten på nytt.", - "about.missing-verification-email.description3": "Hvis e-posten fortsatt ikke kommer frem, vennligst kontakt oss via vårt kontaktskjema.", - "about.missing-verification-email.description4": "Vær oppmerksom på at det tidligere ofte har vært problemer med Microsoft-adresser (f.eks. Live, Outlook, Hotmail, ...).", - "about.missing-verification-email.description5": "I dette tilfellet, prøv først en e-post fra en annen leverandør.", "stats.stations": "rutekart", "warning-alternative-station": "Du er i ferd med å sjekke inn for en avgang på stasjonen :newStation, som ble vist til deg fordi den er nær :searchedStation.", "stationboard.to": "etter", @@ -550,13 +519,6 @@ "messages.exception.maybe-too-fast": "Kanskje du ved et uhell sendte forespørselen flere ganger?", "ics.description": "Mine reiser med traewelling.de", "profile.no-visible-statuses": "Dessverre er turene til :username ikke synlige for deg.", - "about.dev": "Jeg vil også gjerne være med å utvikle Träwelling!", - "about.dev.1": "Träwelling er et åpen kildekode-prosjekt og alle kan bidra.", - "about.dev.2": "Du kan finne all informasjon om det på vår GitHub-side.", - "about.points-real-time": "Hvis forbindelsen din er forsinket, bør du sørge for å sjekke inn før faktisk avgangstid, da vi ikke lenger kan få sanntidsinformasjon om reisen etter selve avgangen.", - "about.dev.3": "Vi ser frem til hvert bidrag - bare lag en Pull Request!", - "about.dev.4": "Du kan enten jobbe med dine egne ideer eller velge en oppgave fra problemene våre på GitHub.", - "about.dev.5": "Hvis du har spørsmål, kan du gjerne kontakte oss på vår Discord-server: :link", "email.verification.too-many-requests": "Vent noen minutter før du forespør en ny bekreftelse via e-post.", "privacy.sign.more": "Godta og fortsett til Träwelling", "settings.delete-account.more": "Avvis og slett konto igjen", diff --git a/lang/nl.json b/lang/nl.json index d628f305d..cea0a77ee 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -1,27 +1,14 @@ { "about.express": "InterCity, EuroCity", - "about.faq-heading": "Veelgestelde vragen", - "about.basepoints": "Kernpunten", "about.block1": "Träwelling is een gratis incheckdienst waarmee je je vrienden kunt laten weten waar je momenteel bent en gebruikmaakt van openbaar vervoer. Ook kun je dit alles bijhouden in een logboek. Kortom: check in in treinen en verdien punten.", "about.feature-missing": "Als je een idee voor een functie of een bug gevonden hebt, meld deze dan op onze GitHub-repo.", "about.feature-missing-heading": "Hoe meld ik fouten of deel ik ideeën?", - "about.heading": "Over Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.points-heading": "Hoe worden de punten berekend?", - "about.calculation": "De afstand wordt afgerond op de dichtstbijzijnde 10 km en vervolgens gedeeld door 10. Vervolgens worden de kernpunten erbij opgeteld.
Voorbeeld: een ICE-rit van 143 km levert 10 + afronding(143/10) = 10 + 15 = 25 punten op; een S-Bahn-rit van 8 km 2 + afronding(8/10) = 2 + 1 = 3 punten.
We willen dat je zo snel mogelijk incheckt, zodat het platform altijd op de hoogte is van de actuele status van je reizen. Daarom krijg je alleen het volledige aantal punten als je incheckt tijdens je reis of 20 min. voor vertrek (gepland vertrek + vertraging). Als je 1 uur voor of na de reisperiode incheckt, krijg je slechts 1/4 van de punten. Als je nóg eerder of later incheckt, dan worden de volledige kilometers en uren bijgeschreven, maar krijg je slechts één punt uit medelijden.", - "about.name": "De naam is een verwijzing naar het bekende “Senk ju for träwelling wis Deutsche Bahn”, dat je in bijna elke langeafstandstrein van Deutsche Bahn wel eens kan hebben gehoord.", - "about.name-heading": "Waar komt de naam vandaan?", - "about.no-train": "We gebruiken een dienst van Deutsche Bahn, waar niet alle verbindingen rechtstreeks worden getoond. Helaas kunnen we niet veel doen als jouw trein er niet bij zit.", - "about.no-train-heading": "Waarom staat mijn trein niet op de lijst?", - "about.points1": "De punten zijn samengesteld uit de productklasse en de afstand van je reis.", - "about.productclass": "Productklasse", "about.tram": "Tram, lightrail, bus, metro", "about.suburban": "Voorstadstreinen, Veerboten", "admin.usage": "Gebruik", "admin.select-range": "Kies een gebied", "about.regional": "Regionale (snel)treinen", - "about.who": "Träwelling woord sinds 2013 door verschillende mensen ontwikkeld. Deels alleen bugfixes, deels grotere veranderingen. Meer informatie vindt je in de", - "about.who-heading": "Wie ontwikkelt Träwelling?", "admin.greeting": "Hoi", "admin.usage-board": "Gebruiksevaluatie", "admin.registered-users": "Geregistreerde gebruikers", @@ -410,17 +397,12 @@ "stats.time-in-minutes": "Reistijd (in minuten)", "stats.global.distance": "Totale reisafstand van alle Träwelling-gebruikers", "description.profile": ":username heeft in :hourAmount uur tijd al :kmAmount kilometer afgelegd met het openbaar vervoer.", - "support.success": "Je verzoek is ingediend en genoteerd met ticketnummer #:ticketNumber. We zullen proberen je zo snel mogelijk te antwoorden.", "data-may-incomplete": "De informatie kan onvolledig of onjuist zijn.", "empty-en-route": "Er zijn momenteel geen träwellers op reis.", "overlapping-checkin.force-yes": "Ja, forceren.", "request-feature": "Functieverzoek", - "about.dev": "Ik wil ook helpen om Träwelling te verbeteren!", "exit": "Uitstap", "description.en-route": "Overzichtskaart van alle träwellers die momenteel onderweg zijn.", - "support": "Ondersteuning", - "support.create": "Ondersteuningsverzoek indienen", - "support.submit": "Verzoek indienen", "go-to-settings": "Instellingen openen", "search-results": "Zoekresultaten", "settings.visibility.default": "Check-ins: standaard zichtbaarheid", @@ -442,23 +424,15 @@ "export.title.destination.time.real": "Daadwerkelijke aankomsttijd", "stats.range.days": "Afgelopen :days dagen", "stats.range.picker": "Kies een periode", - "to-support": "Ondersteuningsverzoek", "description.leaderboard.main": "De topträwellers van de afgelopen 7 dagen.", - "about.dev.4": "Je kunt aan je eigen ideeën werken of een taak kiezen uit de ‘issues’ op GitHub.", - "about.dev.5": "Als je vragen hebt, neem dan gerust contact met ons op via onze Discordserver: :link", "overlapping-checkin": "Overlappende check-in", "support.privacy": "Gegevensbescherming", - "about.who2": "Op GitHub vind je een lijst met bijdragers.", "email.verification.too-many-requests": "Wacht een paar minuten voordat je een nieuwe bevestigingsmail aanvraagt.", "support.privacy.description": "Je gebruikersnaam, gebruikers-id en e-mailadres worden alleen op jouw verzoek in ons ticketsysteem vastgelegd.", "checkin.points.forced": "Je hebt voor deze check-in geen punten verdiend, omdat je de check-in hebt geforceerd.", "auth.required": "Log in om deze functie te kunnen gebruiken.", "export.error.time": "Je kunt alleen ritten exporteren gedurende een periode van maximaal 365 dagen.", - "about.who0": "Träwelling is een opensourceproject.", "christmas-mode": "Kerstmodus", - "about.who1": "Sinds 2013 werken er verschillende personen aan het project. Deels als bugoplosser en deels als grote veranderaar.", - "about.who3": "Daarnaast heeft een aantal mensen bijgedragen aan de alfaversie. Een lijst met deze mensen vind je hier.", - "login-required": "(Inloggen vereist)", "notifications.eventSuggestionProcessed.lead": "Je evenementsuggestie, :name, is verwerkt.", "settings.delete-account.more": "Weigeren en account verwijderen", "merry-christmas": "We wensen je fijne kerstdagen!", @@ -469,11 +443,6 @@ "settings.revoke-token.success": "De toegang is ingetrokken", "no-points-message.forced": "Je hebt niet het volledige aantal punten verdiend omdat je de check-in geforceerd hebt.", "settings.visibility.disclaimer": "Als je profiel privé is, kunnen alleen volgers je statussen zien, óók je openbare statussen.", - "about.events.description1": "Träwelling brengt mensen samen die samen gebruikmaken van het openbaar vervoer. Soms gaan ze zelfs naar hetzelfde evenement zonder dat ze dat van elkaar weten!", - "about.events.description2": "Daarom hebben we de evenmentfunctie gemaakt. Een ieder kan via ons formulier een evenement voor een vastgelegde periode aanbevelen, en alle gebruikers van Träwellig kunnen de check-ins aan het evenement in kwestie koppelen.", - "about.events.description3": "Als je een evenement wilt aanmaken, houd er dan rekening mee dat evenementen een toegevoegde waarde moeten hebben voor de hele Träwelling-gemeenschap. Dit omvat spoorwegevenementen, zoals de laatste reis van de Metropolitan, evenementen van hackerspaces (bijv. de GPN), of uit de LGBTQ-gemeenschap, zoals de Cologne Pride.", - "about.missing-verification-email": "Waarom heb ik geen bevestingsmail ontvangen?", - "about.missing-verification-email.description2": "Als de e-mail niet in de spammapbeland is, voeg dan ‘:email’ aan je contactpersonen en vraag een nieuwe bevestigingsmail aan.", "support.go-to-github": "Meld bugs en ideeën op onze GitHub-pagina. Gebruik hiervoor onderstaande knoppen. We kunnen je helpen met dit formulier als je problemen hebt met je account of check-ins op .", "transport_types.taxi": "Taxi", "stats.categories": "Vervoerssoorten tijdens je reizen", @@ -484,14 +453,12 @@ "stats.global.duration": "Reistijd van alle Träwelling-gebruikers", "stats.global.explain": "De globale statistieken bevatten alle check-ins van Träwellers in de periode van :fromDate tot :toDate.", "stats.global.active": "Actieve Träwelling-gebruikers", - "support.answer": "We zullen je zo snel mogelijk verder helpen. Onze antwoorden zullen per e-mail naar :adress worden verstuurd (in het Duits of Engels).", "leaderboard.notice": "De hier getoonde gegevens zijn gebaseerd op de check-ins van de afgelopen 7 dagen. Het bijwerken van de topscorelijst kan tot vijf minuten duren.", "how-can-we-help": "Hoe kunnen we je helpen?", "christmas-mode.disable": "Kerstmodus in deze sessie uitschakelen", "localisation.not-available": "Deze inhoud is momenteel niet in jouw taal beschikbaar.", "no-points-warning": "Je verdient hiervoor niet het volledige aantal punten.", "overlapping-checkin.description": "Je reis kan niet worden opgeslagen omdat deze overlapt met je check-in in :lineName.", - "about.missing-verification-email.description": "Controleer of de e-mail in de spammap beland is.", "event": "Evenement", "user.profile-picture": "Profielfoto van @:username", "menu.show-all": "Alles tonen", @@ -515,7 +482,6 @@ "settings.heading.profile": "Profiel aanpassen", "settings.saved": "De wijzigingen zijn opgeslagen", "status.visibility.2": "Alleen voor volgers", - "support.email-required": "We kunnen pas contact met je opnemen nadat je je e-mailadres hebt geverifieerd.", "subject": "Onderwerp", "request-time": "Opgevraagd om :time", "checkin.conflict.question": "Wil je tóch inchecken? Je verdient hiervoor geen punten, maar je persoonlijke statistiek wordt wel bijgewerkt.", @@ -567,15 +533,6 @@ "overlapping-checkin.description2": "Wil je het inchecken forceren?", "overlapping-checkin.force-no": "Nee, niets doen.", "report-bug": "Bug melden", - "about.events": "Wat zijn evenementen?", - "about.events.description4": "Evenementen moeten iets bijzonders zijn - daarom hebben we besloten om kleine lokale evenementen zoals kerstmarkten en stadsfeesten principieel af te wijzen. Uitzonderingen bevestigen de regel.", - "about.missing-verification-email.description3": "Mocht de e-mail nog steeds niet aankomen, neem dan contact met ons op via ons contactformulier.", - "about.missing-verification-email.description4": "Houd er rekening mee dat er in het verleden vaak problemen zijn geweest met Microsoftadressen (Live, Outlook, Hotmail, etc.).", - "about.missing-verification-email.description5": "Probeer in dat geval eerst een e-mailadres van een andere provider.", - "about.points-real-time": "Als je verbinding vertraagd is, zorg er dan voor dat je vóór de daadwerkelijke vertrektijd incheckt, aangezien we na het daadwerkelijke vertrek geen realtime informatie meer kunnen opvragen.", - "about.dev.1": "Träwelling is een opensourceproject waaraan iedereen kan bijdragen.", - "about.dev.2": "Alle informatie hieromtent is te vinden op onze GitHub-pagina.", - "about.dev.3": "We kijken uit naar elke bijdrage - maak gewoon een PullRequest aan!", "other": "Overig", "platform": "Spoor", "real-time-last-refreshed": "Live-informatie, laatst bijgewerkt om", @@ -642,7 +599,6 @@ "user.likes-enabled": "Vind-ik-leuks tonen", "menu.oauth_authorize.scopes_title": "Deze app vereist de volgende rechten:", "scopes.write-notifications": "Markeer alle meldingen als ongelezen en wis ze", - "scopes.write-support-tickets": "Maak ondersteuningsverzoeken aan", "scopes.write-settings-privacy": "Wijzig je privacy-instellingen", "menu.oauth_authorize.third_party": "Deze app is geen officiële Träwelling-app!", "menu.oauth_authorize.third_party.more": "Wat houdt dit in?", @@ -669,7 +625,7 @@ "scopes.extra-delete": "Verwijder je Träwelling-account", "notifications.readAll.success": "Alle meldingen zijn als gelezen gemarkeerd.", "notifications.socialNotShared.mastodon.0": "Er is een onbekende fout opgetreden tijdens het tooten.", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "map-providers.open-railway-map": "Open Railway Map", "user.mapprovider": "Kaartleverancier", "settings.twitter-deprecated": "Lees de verouderingsmelding", @@ -812,5 +768,17 @@ "action.error": "Deze actie kan niet worden uitgevoerd - probeer het later opnieuw.", "action.like": "Vind-ik-leukstatus", "action.dislike": "Vind-ik-niet-leukstatus", - "action.set-home": "Mijn station instellen" + "action.set-home": "Mijn station instellen", + "transport_types.plane": "Vliegtuig", + "trip_creation.limitations.6": "Let op: slechts een beperkt aantal voertuigen is toegestaan (reizen per auto, fiets of te voet zijn bijvoorbeeld niet toegestaan en kunnen worden verwijderd). Zie ook:", + "dashboard.empty": "Je overzicht lijkt erg leeg.", + "dashboard.empty.teaser": "Als je wilt, kun je mensen volgen en hier zien waar ze zijn ingecheckt.", + "dashboard.empty.discover1": "Zoek personen om te volgen in de sectie", + "dashboard.empty.discover2": "of", + "dashboard.empty.discover3": "(let op: lange laadtijden)", + "trip_creation.limitations.6.rules": "Regels", + "trip_creation.limitations.6.link": "https://help.traewelling.de/en/features/manual-trips/#info", + "user.points-enabled": "Punten en klassement tonen", + "checkin.success.body2": "Je legt :distance km af op :lineName, van :origin naar :destination.", + "checkin.success.body": "Je bent ingecheckt!" } diff --git a/lang/pl.json b/lang/pl.json index c13251b2c..bdcf3da74 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -1,18 +1,9 @@ { - "about.basepoints": "punkty podstawowe", "about.block1": "Träwelling to darmowa usługa meldunkowa, która pomoże ustalić innym gdzie jesteś i zapisuje twoje podróże transportem publicznym. W pigułce: meldujesz się w pociągu i otrzymujesz punkty.", - "about.faq-heading": "Najczęściej zadawane pytania", "about.feature-missing-heading": "Czegoś brakuje! Dlaczego ta funkcja została usunięta?", - "about.heading": "O Träwelling", "about.international": "Koleje dużych prędkości: ICE, TGV, RailJet", - "about.name-heading": "Skąd pochodzi nazwa?", - "about.no-train-heading": "Dlaczego nie ma mojego połączenia?", - "about.points-heading": "Jak są obliczane punkty?", - "about.points1": "Na ilość punktów składają się kategoria pojazdu oraz odległość przejazdu.", - "about.productclass": "Kategoria", "about.suburban": "SKM, prom", "about.tram": "tramwaj (w tym Stadtbahn), autobus, metro", - "about.who-heading": "Kto tworzy Träwelling?", "admin.greeting": "Witaj", "admin.usage": "Użycie", "admin.select-range": "Wybierz przedział", @@ -51,7 +42,7 @@ "controller.user.follow-request-ok": "Wysłano prośbę o obserwowanie.", "controller.user.password-changed-ok": "Zmieniono hasło.", "controller.user.password-wrong": "Błędne hasło.", - "user.complete-registration": "Zakończono rejestrację", + "user.complete-registration": "Zakończ rejestrację", "user.displayname": "Wyświetlany pseudonim", "user.email": "Adres e-mail", "user.email-verify": "Zweryfikuj adres e-mail", @@ -153,7 +144,7 @@ "menu.logout": "Wyloguj się", "menu.ohno": "O nie!", "menu.privacy": "Ochrona danych", - "menu.profile": "Profilowa", + "menu.profile": "Profil", "menu.register": "Zarejestruj się", "menu.settings": "Ustawienia", "menu.settings.follower-requests": "Prośby o obserwowanie", @@ -168,7 +159,7 @@ "pagination.back": "« Wróć", "passwords.reset": "Zresetowano twoje hasło.", "privacy.title": "Polityka prywatności", - "privacy.we-changed": "Zmieniliśmy naszą politykę prywatności. Żeby kontynuować korzystanie z usługi, musisz ją zaakceptować. Jeżeli nie wyrażasz zgody, możesz usunąć konto przyciskiem poniżej.", + "privacy.we-changed": "Zmieniliśmy naszą politykę prywatności. Żeby kontynuować korzystanie z usługi, musisz ją zaakceptować. Jeżeli nie wyrażasz zgody, możesz usunąć konto, wciskając przycisk poniżej.", "privacy.sign": "Wyrażam zgodę", "profile.follow": "Obserwuj", "profile.follow_req.pending": "Oczekuje", @@ -264,7 +255,6 @@ "user.unmute-tooltip": "Odcisz", "dashboard.future": "Twoje przyszłe meldunki", "about.express": "InterCity, EuroCity, Express InterCity", - "about.name": "Nazwa odwołuje się do komunikatu \"Senk ju for träwelling wis Deutsche Bahn\" (z ang., niemiecki akcent: Dziękujemy za podróż z Deutsche Bahn), który można usłyszeć w prawie każdym dalekobieżnym pociągu Deutsche Bahn.", "controller.social.already-connected-error": "Konto już jest połączone z innym użytkownikiem.", "controller.status.like-not-found": "Nie znaleziono polubienia.", "about.feature-missing": "Jeśli chcesz zasugerować nam nową funkcję lub znalazłeś błąd, zgłoś to bezpośrednio na naszym repozytorium GitHub.", @@ -276,12 +266,10 @@ "user.fresh-link": "Nowy link weryfikacji został wysłany na adres e-mail.", "user.not-received-before": "Jeżeli nie otrzymałeś wiadomości na e-mail, ", "user.please-check": "Przed przejściem dalej, sprawdź swój e-mail po wiadomość z linkiem aktywacyjnym.", - "about.calculation": "Odległość zaokrągrągla się w górę do 10km, a następnie dzieli przez 10. Potem dodaje się punkty podstawowe.
Podróż ICE na odległość 143km daje 10 + zaokrąglenie w górę(143/10) = 10 + 15 = 25 punktów, dla ośmiokilometrowej podróży SKM to jest 2 + zaokrąglenie w górę(8/10) = 2 + 1 = 3 punktów.
Chcemy, abyś zameldował swój przejazd jak najbliżej godziny odjazdu, aby na platformie był aktualny status twojej podróży. Dlatego otrzymasz pełną ilość punktów, tylko jeżeli to wykonasz 20 minut przed odjazdem, bądź w trakcie podróży (w wybranych krajach wlicza się także opóźnienie). Jeżeli zameldujesz się w ciągu godziny po przyjeździe/przed przyjazdem, otrzymasz ćwierć możliwych do zdobycia punktów. Poza tymi ramami czasowymi twój czas i dystans zapisze się w pełni, ale zostanie przyznany tylko 1 punkt.", "user.remember-me": "Zapamiętaj hasło", - "about.who": "Od 2013, Träwelling tworzyło wielu różnych ludzi. Częściowo jedynie poprawiali, częściowo wprowadzali większe zmiany. Znajdziesz więcej informacji w pliku", - "user.reset-pw-link": "Zresetuj hasło", + "user.reset-pw-link": "Wyślij link do zresetowania hasła", "user.username": "Nazwa użytkownika", - "user.forgot-password": "Zapomniałeś hasła?", + "user.forgot-password": "Nie pamiętasz hasła?", "leaderboard.user": "Użytkownik", "user.liked-status": "polubił ten status.", "leaderboard.no_data": "Brak danych za ten miesiąc.", @@ -319,9 +307,9 @@ "notifications.mark-as-unread": "Oznacz jako nieprzeczytane", "notifications.mark-as-read": "Oznacz jako przeczytane", "notifications.statusLiked.notice": "Podróż :line dnia :createdDate|Podróż linią :line dnia :createdDate", - "notifications.userFollowed.lead": "@:followerUsername obserwuje cię.", + "notifications.userFollowed.lead": "@:followerUsername obserwuje Cię.", "notifications.userRequestedFollow.lead": "@:followerRequestUsername chce cię obserwować.", - "notifications.userApprovedFollow.lead": "@:followerRequestUsername zaakceptował twoją prośbę o obserwowanie", + "notifications.userApprovedFollow.lead": "Twoja prośba obserwacji została zaakceptowana przez użytkownika @:followerRequestUsername.", "notifications.socialNotShared.lead": "Twój meldunek nie został udostępniony na :Platform.", "notifications.socialNotShared.mastodon.401": "Twoja instancja odesłała błąd 401 Unauthorized kiedy próbowaliśmy wysłać toota. Może spróbuj ponownie połączyć Mastodon z Träwelling?", "user.invalid-mastodon": ":domain nie wygląda na link do instancji Mastodona.", @@ -331,8 +319,8 @@ "passwords.password": "Hasło musi mieć przynajmniej 6 znaków i być zgodne z jego potwierdzeniem.", "passwords.sent": "Wysłaliśmy wiadomość e-mail z linkiem do resetu hasła.", "passwords.token": "Ten link do resetu hasła jest nieważny.", - "passwords.user": "Nie możemy znaleźć użytkownika z tym adresem e-mail", - "privacy.not-signed-yet": "Nie wyraziłeś jeszcze zgody na naszą politykę prywatności. Żeby używać Träwelling, musisz ją zaakceptować. Jeżeli nie wyrażasz zgody, możesz usunąć konto przyciskiem poniżej.", + "passwords.user": "Nie możemy znaleźć użytkownika z tym adresem e-mail.", + "privacy.not-signed-yet": "Nie wyraziłeś jeszcze zgody na naszą politykę prywatności. Żeby używać Träwelling, musisz ją zaakceptować. Jeżeli nie wyrażasz zgody, możesz usunąć konto, wciskając przycisk poniżej.", "profile.follow_req": "Zapytaj o zgodę", "profile.no-statuses": ":username jeszcze nie zameldował żadnych podróży.", "profile.private-profile-text": "Ten profil jest prywatny.", @@ -388,8 +376,7 @@ "user.muted.heading": "Użytkownik wyciszony", "user.muted.heading2": "Wyciszeni użytkownicy", "user.already-unmuted": "Użytkownik :username nie jest wyciszony.", - "about.no-train": "Korzystamy z danych Deutsche Bahn, które mogą nie pokazywać połączeń poprawnie, bądź wcale. Nie możemy zatem nic zrobić, kiedy brakuje twojego połączenia. W Polsce są pokazywane przede wszystkim połączenia kolejowe, wyjątkowo też przygraniczne autobusy wjeżdżające do Niemiec.", - "admin.hafas-entries-by-polylines": "Wpisy HAFAS i ilość przynależnych Polylines", + "admin.hafas-entries-by-polylines": "Wpisy HAFAS i ilość przynależnych polylines", "controller.social.delete-set-password": "Musisz ustawić hasło przed usunięciem powiązania konta, aby zapobiec zablokowaniu.", "controller.social.delete-set-email": "Musisz ustawić adres e-mail przed usunięciem powiązania konta, aby zapobiec zablokowaniu.", "controller.social.delete-never-connected": "Nie masz powiązanych kont innych serwisów.", @@ -419,15 +406,13 @@ "status.visibility.3.detail": "Widoczny tylko dla ciebie", "status.visibility.0.detail": "Widoczny dla wszystkich, w panelu głównym, przy wydarzeniach, itd.", "transport_types.regionalExp": "Regionalexpress/TLK", - "status.visibility.1.detail": "Widoczny dla wszystkich, tylko w profilu", + "status.visibility.1.detail": "Widoczny dla wszystkich, tylko w Twoim profilu", "status.visibility.2.detail": "Tylko zatwierdzeni obserwujący", "date-format": "DD.MM.YYYY", "time.hours.short": "godz", "time.days.short": "dni", "time.days": "dni", "time.years": "lat(a)", - "about.events": "Czym są wydarzenia?", - "about.events.description1": "Träwelling łączy ludzi, którzy korzystają z tego samego transportu publicznego. Zdarza się, że ci ludzie jeżdżą na te same wydarzenia, nie wiedząc o tym!", "menu.show-all": "Pokaż wszystko", "menu.loading": "Ładowanie", "search-results": "Wyniki wyszukiwania", @@ -472,7 +457,6 @@ "time.minutes": "minut(y)", "time.hours": "godzin(y)", "time.months": "miesięcy", - "login-required": "(Wymaga zalogowania się)", "export.reason.private": "Podróż prywatna", "settings.title-change-password": "Zmień hasło", "user.block-tooltip": "Zablokuj użytkownika", @@ -522,11 +506,8 @@ "carriage": "Karoca", "overlapping-checkin.force-yes": "Tak, wykonaj.", "overlapping-checkin.force-no": "Nie rób nic.", - "to-support": "Zapytanie do wsparcia", "request-feature": "Zaproponuj funkcjonalność", - "about.missing-verification-email": "Dlaczego nie otrzymałam/otrzymałem potwierzenia e-mail?", "christmas-mode.disable": "Wyłączono świąteczny tryb dla tej sesji", - "about.dev": "Chcę wnieść swój wkład do Träwelling!", "stats.stations.passed": "Mijana stacja", "stats.stations.changed": "Wpisz / Wyjdź / Zmień", "year-review": "Rok w przeglądzie", @@ -545,7 +526,6 @@ "menu.oauth_authorize.application_information.privacy_policy": "Polityka prywatności :client", "profile.youre-blocking-text": "Zablokowano @:username.", "christmas-mode.enable": "Włączono świąteczny tryb dla tej sesji", - "about.dev.5": "Jeśli masz jakiekolwiek pytania, zapraszamy do kontaktu na naszym serwerze discord: :link", "ics.description": "Moje podróże z traewelling.de", "settings.colorscheme.dark": "Ciemny", "empty-input-disable-function": "Pozostaw to pole pusty, aby wyłączyć tę funkcję.", @@ -565,14 +545,6 @@ "scopes.read-statistics": "zobacz swoje statystyki", "scopes.read-search": "wyszukaj w Träwelling", "user.mapprovider": "Dostawca map", - "about.missing-verification-email.description": "Przede wszystkim sprawdź, czy wiadomość e-mail nie trafiła do spamu.", - "about.missing-verification-email.description4": "Prosimy mieć na uwadze, że w przeszłości występowały częste problemy z adresami e-mail firmy Microsoft (np. Live, Outlook, Hotmail, itd.).", - "about.missing-verification-email.description2": "Jeżeli wiadomość e-mail nie trafiła do spamu, prosimy dodać ':email' do swoich danych kontaktowych i ponownie poprosić o wiadomość e-mail.", - "about.missing-verification-email.description3": "Jeżeli wiadomość e-mail nadal do ciebie nie dociera, prosimy się z nami skontaktować przez formularz kontaktowy.", - "about.missing-verification-email.description5": "W takim przypadku prosimy o użycie adresu e-mail innego dostawcy.", - "about.who2": "Na GitHubie można zobaczyć listę współtwórców kodu źródłowego.", - "about.who0": "Träwelling to projekt open-source.", - "about.who1": "Od 2013 różni ludzie rozwijali ten projekt. Niektórzy tylko naprawiali błędy, inni wprowadzali duże zmiany.", "active-journeys": "aktywna podróż|aktywne podróże", "settings.colorscheme.set": "Zmień paletę kolorów", "settings.colorscheme.light": "Jasny", @@ -582,10 +554,10 @@ "events.disclaimer.warranty": "Träwelling nie gwarantuje poprawności ani całości danych.", "settings.delete-all-tokens": "Usuń wszystkie tokeny", "settings.twitter-deprecated": "Przeczytaj powiadomienie o wycofaniu", - "map-providers.cargo": "Cargo", + "map-providers.cargo": "Carto", "changelog": "Lista zmian", "time-is-manual": "Czas został nadpisany ręcznie", - "notifications.eventSuggestionProcessed.duplicate": "Twoja sugestia już instnieje.", + "notifications.eventSuggestionProcessed.duplicate": "Twoja sugestia już istnieje.", "settings.title-security": "Bezpieczeństwo", "settings.title-extra": "Dodatkowe", "settings.back-to-traewelling": "Wróć do Träwelling", @@ -603,5 +575,37 @@ "time-is-planned": "Zaplanowany czas", "create-app": "Stwórz aplikację", "your-apps": "Twoje aplikacje", - "edit-app": "Edytuj aplikację" + "edit-app": "Edytuj aplikację", + "notifications.readAll.success": "Wszystkie powiadomienia zostały oznaczone jako przeczytane.", + "modals.setHome-body": "Czy chcesz ustawić stację :stationName jako stację macierzystą?", + "notifications.show": "Pokaż powiadomienia", + "settings.experimental.description": "Funkcje eksperymentalne nie są jeszcze gotowe i mogą zawierać błędy. Nie są one rekomendowane do codziennego użytku.", + "notifications.mastodon-server.lead": "Wystąpił problem z Twoją instancją Mastodona.", + "privacy.sign.more": "Akceptuj i kontynuuj do Träwelling", + "stationboard.events-propose": "Możesz zaproponować wydarzenie tutaj:", + "stationboard.timezone.settings": "Jeśli chcesz, możesz to zmienić w ustawieniach.", + "stationboard.events-none": "Nie znaleziono wydarzeń.", + "transport_types.plane": "Samolot", + "settings.visibility.disclaimer": "Jeśli Twój profil jest prywatny, nawet statusy oznaczone jako publiczne będą widoczne tylko dla Twoich obserwujących.", + "settings.visibility.default": "Meldunki: domyślna widoczność", + "user.email.not-set": "Nie ma jeszcze zapisanego adresu e-mail.", + "messages.exception.general-values": "Wystąpił nieznany błąd. Spróbuj ponownie z innymi wartościami.", + "action.set-home": "Ustaw stację macierzystą", + "user.already-unblocked": "Użytkownik :username nie jest zablokowany.", + "user.home-not-set": "Nie ustawiono jeszcze stacji macierzystej.", + "messages.exception.already-checkedin": "To połączenie ma już meldunek.", + "modals.tags.new": "Nowy tag", + "notifications.eventSuggestionProcessed.too-late": "Niestety, Twoja propozycja została wysłana zbyt późno.", + "settings.experimental": "Funkcje eksperymentalne", + "status.update.success": "Twój status został pomyślnie zaktualizowany.", + "user.blocked": "Zablokowano użytkownika :username.", + "user.blocked.text": "Nie możesz zobaczyć meldunków użytkownika :username, ponieważ jest on zablokowany.", + "merry-christmas": "Życzymy Ci Wesołych Świąt!", + "trip_creation.limitations.6.link": "https://help.traewelling.de/en/features/manual-trips/#info", + "export.error.time": "Możesz wyeksportować podróże z okresu co najwyżej 365 dni.", + "messages.exception.maybe-too-fast": "Może Twoje żądanie zostało przypadkiem wysłane wielokrotnie?", + "export.error.amount": "Zażądano eksportu więcej, niż 2000 podróży. Spróbuj zmniejszyć okres do eksportu.", + "messages.exception.hafas.502": "Nie można wczytać rozkładu jazdy. Spróbuj ponownie za chwilę.", + "settings.visibility.hide.explain": "Twoje meldunki będą ustawione jako prywatne po ustawionej przez Ciebie liczbie dni, tylko Ty będziesz w stanie je zobaczyć.", + "stationboard.timezone": "Wykryliśmy, że Twoja strefa czasowa jest inna niż tej stacji. Pokazywane tu czasy są wyświetlane w ustawionej przez Ciebie strefie czasowej :timezone." } diff --git a/lang/sv.json b/lang/sv.json index f449bb73e..80db98a91 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -1,25 +1,12 @@ { - "about.basepoints": "Baspoäng", "about.block1": "Träwelling är en gratis check-in-tjänst du kan använda för att meddela dina vänner var du reser med kollektrafiken och för att ha en logbok. Kort sagt: Man kan checka in på tåg och får poäng för det.", - "about.calculation": "Avstånden blir avrundade uppåt till närmsta 10 km och dividerat med 10. Sedan blir basispoänger adderat.
En ICE resa med en längd av 143 km får du alltså 10 + avrundat uppåt(143/10) = 10 + 15 = 25 poäng, för en resa med pendeltåg (S-Bahn) med en längd av 8 km får du 2 + avrundat uppåt(8/10) = 2 + 1 = 3 poänger.
Du ska checka in så tidigt så möjligt, så att plattformen är alltidt uppdaterad om dina resa. Därför får du bara alla poänger om du checka in mens din resa eller 20 min innan live avgången (planerat avgång + försening). De som checka in en timme innan eller efter tidsrymd av resan får i alla fall 1/4 av poänger. Om du checka in även tidigare eller senare, så får du alla kilometer och timmar, men bara en barmhärtighetspoäng.", "about.express": "InterCity, EuroCity", - "about.faq-heading": "vanliga frågor", "about.feature-missing": "Om du vill föreslå en funktion eller hittat ett fel, rapportera det direkt till vårt GitHub-förråd.", "about.feature-missing-heading": "Hur rapporterar jag buggar eller förslag på förbättringar?", - "about.heading": "Om Träwelling", "about.international": "InterCityExpress, TGV, RailJet", - "about.name": "Namnet är en anspelning på det välkända \"Senk ju för träwelling wis Deutsche Bahn\", som du kanske ha hört på nästan varje fjärrtrafiktåg av Deutsche Bahn.", - "about.name-heading": "Varifrån kommer namnet?", - "about.no-train": "Vi använder ett programming interface från Deutsche Bahn, där inte alla erbjudanden visas direkt. Tyvärr finns det inte mycket vi kan göra om ditt tåg inte är där.", - "about.no-train-heading": "Varför visas inte mitt tåg?", - "about.points-heading": "Hur beräknas poängen?", - "about.points1": "Poängen består av produktklass och avstånd av din resa.", - "about.productclass": "Produktklass", "about.regional": "Regionaltåg (RB) / Regionalexpress (RE)", "about.suburban": "S-tåg, färja", "about.tram": "Spårvagn / lätt järnväg, buss, tunnelbana", - "about.who": "Träwelling har utvecklas av flera olika människor sedan 2013. Ibland bara buggfixar, några större förändringar. Mer detaljerad information, kolla in", - "about.who-heading": "Vem utvecklar Träwelling?", "auth.failed": "Det här kontot existerar inte eller data har skrivits in felaktigt.", "auth.throttle": "För många inloggningsförsök. Försök igen om :seconds sekunder.", "controller.social.already-connected-error": "Det här kontot är redan associerat till en annan användare.", @@ -475,19 +462,13 @@ "settings.ics.descriptor": "Här kan du hantera dina ICS-länkar. På så sätt kan du visa dina tidigare resor i en kalender som stöder det.", "settings.ics.modal": "Utfärdade ICS-tokens", "generic.change": "Förändra", - "support.create": "Skapa en supportförfrågan", - "support.submit": "Skicka supportförfrågan", "go-to-settings": "Till inställningarna", "subject": "referens", "how-can-we-help": "Hur kan vi hjälpa?", - "support.success": "Vi har tagit emot din förfrågan (biljettnummer: #:ticketNumber). Vi kommer att svara dig via e-post så snart som möjligt.", "support.privacy": "Information om dataskydd", "support.privacy.description": "Ditt användar-ID, användarnamn och e-postadress kommer att registreras i vårt biljettsystem med din förfrågan.", "support.privacy.description2": "Uppgifterna raderas efter ett år oavsett ditt användarkonto hos Träwelling.", "settings.revoke-token.success": "Åtkomsten har återkallats", - "support.email-required": "Du behöver en verifierad e-postadress för att skicka en supportförfrågan. Detta är det enda sättet vi kan svara dig på.", - "support.answer": "Vi svarar dig så snart som möjligt. Du får svaret till din e-postadress: adress.", - "support": "Support", "generic.why": "Varför?", "checkin.points.could-have": "Du kunde ha fått fler poäng om du checkade in närmare den verkliga avgångstiden!", "checkin.points.forced": "Du fick inga poäng för denna incheckning eftersom du tvingade fram den.", @@ -536,27 +517,8 @@ "no-points-message.forced": "Du fick inte hela poängen för att tvinga fram incheckningen.", "overlapping-checkin.force-yes": "Ja, tvinga.", "overlapping-checkin.force-no": "Nej, gör ingenting.", - "about.who0": "Träwelling är ett projekt med öppen källkod.", - "about.who1": "Olika personer har utvecklat projektet sedan 2013. Vissa bara buggfixar, vissa större förändringar.", - "about.who2": "Du kan se en lista över kodbidragsgivare på GitHub.", - "about.who3": "Dessutom hjälpte några personer till med alfaversionen. Du hittar dem här.", - "login-required": "(Inloggning Krävs)", "report-bug": "Rapportera ett fel", - "to-support": "Gå till supporten", "request-feature": "Önskar Funktion", - "about.missing-verification-email": "Varför får jag inget bekräftelsemail?", - "about.missing-verification-email.description": "Kontrollera först och främst om e-postmeddelandet finns i skräppostmappen.", - "about.missing-verification-email.description2": "Om e-postmeddelandet inte finns i skräppostmappen, lägg till ':email' i dina kontakter och begär e-postmeddelandet igen.", - "about.missing-verification-email.description3": "Om e-postmeddelandet fortfarande inte kommer fram, vänligen kontakta oss via vårt kontaktformulär.", - "about.missing-verification-email.description4": "Observera att det tidigare ofta har varit problem med Microsoft-adresser (t.ex. Live, Outlook, Hotmail, ...).", - "about.missing-verification-email.description5": "Försök i så fall att skicka ett e-postmeddelande från en annan leverantör först.", - "about.dev": "Jag skulle också vilja vara med och utveckla Träwelling!", - "about.dev.1": "Träwelling är ett projekt med öppen källkod och alla kan bidra.", - "about.dev.2": "Du kan hitta all information om det på vår GitHub-sida.", - "about.dev.3": "Vi ser fram emot varje bidrag - skapa bara en Pull Request!", - "about.dev.4": "Du kan antingen arbeta med dina egna idéer eller välja en uppgift från våra problem på GitHub.", - "about.dev.5": "Om du har frågor är du välkommen att kontakta oss på vår Discord-server: :link", - "about.points-real-time": "Om din anslutning är försenad bör du se till att checka in innan den faktiska avgångstiden, eftersom vi inte längre kan få realtidsinformation om resan efter den faktiska avgången.", "email.verification.too-many-requests": "Vänta några minuter till innan du begär en ny bekräftelse via e-post.", "privacy.sign.more": "Acceptera och fortsätt till Träwelling", "settings.delete-account.more": "Avvisa och ta bort konto igen", @@ -569,8 +531,6 @@ "notifications.eventSuggestionProcessed.lead": "Ditt eventförslag :name har redigerats.", "notifications.eventSuggestionProcessed.denied": "Ditt förslag avvisades.", "notifications.eventSuggestionProcessed.accepted": "Ditt förslag accepterades.", - "about.events": "Vad är evenemang?", - "about.events.description4": "Evenemang ska vara något speciellt. Därför bestämde vi oss för att av princip avvisa små lokala evenemang som julmarknader och stadsfester. Undantag bekräftar regeln.", "platform": "Spår", "real-time-last-refreshed": "Realtidsdata senast uppdaterad", "help": "Hjälp", @@ -586,8 +546,5 @@ "year-review.open": "Se året i redovisningen", "year-review.teaser": "Året går mot sitt slut och vi har upplevt mycket tillsammans. Ta en titt på din årliga översyn av Träwelling nu:", "stationboard.check-chainPost": "Bifoga till den senast publicerade incheckningen", - "experimental-features": "Experimentella funktioner", - "about.events.description1": "Träwelling samlar människor som åker kollektivt. Ibland går de till och med på samma evenemang utan att känna till varandra!", - "about.events.description2": "Det är därför vi integrerade evenemangsfunktionen. Vem som helst kan skapa ett evenemang för en viss tidsperiod med hjälp av vårt formulär och alla Träwelling-användare kan sedan länka sina incheckningar till evenemanget.", - "about.events.description3": "Om du vill skapa ett evenemang, observera att evenemang bör ha ett mervärde för hela Träwelling-gemenskapen. Detta inkluderar järnvägsevenemang som den sista resan i Metropolitan, evenemang från hackerutrymmen (t.ex. GPN), eller från HBTQ-scenen som Cologne Pride." + "experimental-features": "Experimentella funktioner" } diff --git a/lang/tr.json b/lang/tr.json index 44b0db66b..c631b1442 100644 --- a/lang/tr.json +++ b/lang/tr.json @@ -1,18 +1,9 @@ { - "about.heading": "Träwelling Hakkında", "about.express": "InterCity, EuroCity", - "about.faq-heading": "Sıkça sorulan sorular", "about.international": "InterCityExpress, TGV, RailJet", - "about.who0": "Träwelling bir açık kaynak projedir.", - "about.name-heading": "İsim nereden geliyor?", "about.regional": "Bölgesel tren/ekspres", - "about.points-heading": "Puanlar nasıl hesaplanıyor?", - "login-required": "(Giriş yapma gerekli)", "about.suburban": "Banliyö, Feribot", "admin.greeting": "Merhaba", - "about.who-heading": "Träwelling'i kim geliştiriyor?", - "about.who1": "2013'ten beri, birçok kişi bu projeyi geliştirmektedir. Kısmen sadece hata düzeltmeleri, kısmen önemli değişiklikler.", - "about.who2": "GitHub üzerinde koda katkı sağlayanların bir listesini bulabilirsiniz.", "admin.usage": "Kullanım", "admin.statuses": "durumlar", "user.displayname": "Görünen ad", @@ -70,11 +61,8 @@ "pagination.back": "« Geri", "passwords.user": "Bu e-posta adresiyle bir kullanıcı bulamıyoruz", "menu.show-more": "Daha fazlasını göster", - "support": "Destek", - "support.submit": "Destek talebini gönder", "how-can-we-help": "Nasıl yardımcı olabiliriz?", "go-to-settings": "Ayarlara git", - "support.answer": "Mümkün olan en kısa sürede size cevap vereceğiz. Yanıt e-posta adresinize gönderilecektir :address.", "settings.visibility": "Görünürlük", "settings.mastodon.visibility": "Mastodon gönderilerinin görünürlüğü", "settings.visibility.disclaimer": "Eğer profiliniz gizliyse, herkese açık olarak işaretlenmiş durumlar bile sadece takipçilerinize gösterilecektir.", @@ -102,22 +90,16 @@ "scopes.read-search": "Träwelling'de ara", "refresh": "Yenile", "language.en": "İngilizce", - "about.no-train-heading": "Niye trenim listelenmiyor?", "support.create": "Destek talebi oluştur", - "support.success": "Talebinizi almış bulunmaktayız (Bilet #:ticketNumber). E-posta yoluyla mümkün olan en kısa sürede size cevap vereceğiz.", "about.tram": "Tramvay / hafif raylı sistem, otobüs, metro", "description.profile": ":username toplu taşımada :hourAmount saatte :kmAmount kilometre seyahat etti.", - "support.email-required": "Destek talebi göndermek için doğrulanmış bir e-posta adresinizin olması gerekmektedir. Ondan sonra size cevap verebiliriz.", "admin.new-users": "yeni kullanıcılar", "welcome": "Träwelling'e hoş geldiniz!", - "about.who3": "Ek olarak, birkaç kişi alfa (test) sürümü ile ilgili olarak yardımcı oldu. Burada hepsini bulabilirsiniz.", "edit": "Düzenle", "menu.oauth_authorize.cancel": "İptal et", "platform": "Platform", "christmas-mode.disable": "Bu oturum için Noel modunu devre dışı bırak", - "about.dev.5": "Eğer herhangi bir sorunuz varsa, discord sunucumuzda bizle iletişim kurmaktan çekinmeyin: :bağlantı", "support.privacy.description": "Kullanıcı ID'niz, kullanıcı adınız ve e-posta adresiniz bilet sistemizde talebinizle birlikte saklanacaktır.", - "about.missing-verification-email": "Niye bir doğrulama postası almıyorum?", "stats.stations": "İstasyon haritası", "time.hours": "saat", "settings.mastodon.visibility.0": "Herkese açık", @@ -154,15 +136,7 @@ "settings.tab.account": "Hesap", "profile.settings": "Ayarlar", "privacy.title": "Gizlilik Politikası", - "about.basepoints": "Taban puanlar", - "about.events": "Etkinlikler nedir?", - "about.events.description1": "Träwelling aynı toplu taşımayı kullanan insanları bir araya getirir. Hatta bazen bu insanlar aynı etkinliklere yol alırlar, birbirlerinden haberleri olmadan!", - "about.events.description2": "İşte bu yüzden Etkinlikler özelliğini oluşturduk. Herkesbelli bir zaman dilimi içerisinde bir etkinlik önerebilir ve kabul edildikten sonra, tüm kullanıcılar kendi check-inlerini o etkinliğe bağlayabilirler.", - "about.events.description4": "Etkinlikler özel şeylerdir. Bu yüzden Noel pazarları veya kent fuarları gibi daha küçük, yerel etkinliklerle ilgili önerileri reddetmeye karar verdik. Her zaman olduğu gibi, bu kurala da istisnalar var.", "about.feature-missing-heading": "Hataları veya geliştirme için önerileri nasıl bildirebilirim?", - "about.name": "Sitenin ismi, Alman Demiryollarının (Deutsche Bahn) neredeyse her uzun mesafe treninde duyabileceğiniz meşhur \"Senk ju for träwelling wis Deutsche Bahn\" anonsuna bir göndermedir.", - "about.points1": "Puanlar servis sınıfından ve yolculuğunuzun uzunluğundan oluşur.", - "about.productclass": "Servis sınıfı", "admin.usage-board": "Kullanım Panosu", "admin.select-range": "Aralık seç", "admin.checkins": "Check-inler", @@ -201,15 +175,12 @@ "dates.decimal_point": ",", "dates.-on-": " tarihinde ", "about.block1": "Träwelling, arkadaşlarınıza nerede olduğunuzu söyleyebileceğiniz ve toplu taşıma yolculuklarınızı kaydedebileceğiniz ücretsiz bir check-in servisidir. Uzun lafın kısası, trenleri kaydedersiniz ve karşılığında puan toplarsınız.", - "about.calculation": "Uzaklık en yakın 10 kilometreye yuvarlanır ve sonrasında 10'a bölünür. Daha sonra taban puanlar eklenir.
Dolayısıyla, 143 kilometrelik bir ICE yolculuğu size 10 + yuvarla(143/10) = 10 + 15 = 25  puan kazandırır; 8 kilometrelik bir banliyö yolculuğu için 2 + yuvarla(8/10) = 2 + 1 = 3  puan kazanırsınız.
Mümkün olan en kısa sürede check-in yapmanızı istiyoruz, böylelikle her zaman platformda yolculuklarınızın güncel durumu olur. Bu yüzden, eğer yolculuğunuz sırasında veya canlı kalkıştan 20 dk önce (planlanan kalkış + gecikme) check in yaparsanız tam puan alırsınız. Eğer yolculuk süresinden 1 saat önce veya sonra check in yaparsanız, hâlâ puanların 1/4'ünü alabilirsiniz. Eğer daha önce veya sonra bir aktarma sırasında check in yaparsanız, tüm kilometre ve saatleri alırsnız, ancak insaflı davranarak sadece 1 puan alırsınız.", "admin.hafas-entries-by-polylines": "HAFAS kayıtları ve karşılık gelen Polylines sayısı", "about.feature-missing": "IEğer bir özellik önermek isterseniz veya bir hata bulduysanız, lütfen doğrudan GitHub depomuza bildirin.", - "about.events.description3": "Eğer bir etkinlik oluşturmak istiyorsanız, lütfen etkinliklerin tüm Träwelling topluluğunun faydasına olduğundan emin olun. Bu etkinlikler Avrupa demiryoluEuropean railway community such as bir tren modelinin son seferi gibi Avrupa demiryolu topluluğundan, GPN gibi hacking etkinliklerinden veya such as Köln Onur Günü gibi LGBTİ topluluğundan olabilir.", "controller.status.like-deleted": "Beğeni silindi.", "controller.status.not-permitted": "Bunu yapmaya izniniz yok.", "controller.social.already-connected-error": "Bu hesap hâlihazırda başka bir kullanıcıya bağlı.", "controller.status.export-neither-business": "Hem şahsi gezi hem de iş gezisi seçimini kaldıramazsınız.", - "about.no-train": "Tüm bağlantıların doğrudan gösterilmediği, Alman Demiryolları (Deutsche Bahn) tarafından sağlanan bir arayüz kullanıyoruz. Eğer treniniz orada bulunmamaktaysa yapabileceğimiz çok fazla bir şey yok. Türkiye'de sadece Bosfor Ekspresi, Sofya Ekspresi, Ankara-İstanbul, İstanbul-Konya ve Ankara-Konya YHT'leri, İzmir Mavi Treni, Konya Mavi Treni, Doğu Ekspresi, Toros Ekspresi ana duraklarda duracak şekilde gösterilmektedir.", "controller.user.follow-request-already-exists": "Bu kişiye hâlihazırda takip isteği gönderdiniz.", "controller.user.follow-destroyed": "Kullanıcıyı takipten çıktınız.", "controller.social.delete-set-email": "Bir SSO sağlayıcısını silmeden önce, hesaba erişiminizi yitirmemek için bir e-posta adresi ayarlamalısınız.", diff --git a/package-lock.json b/package-lock.json index de0608c7a..fbb5371fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,8 @@ "name": "traewelling", "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", - "@vuepic/vue-datepicker": "^8.4.0", - "apexcharts": "^3.46.0", + "@vuepic/vue-datepicker": "^9.0.1", + "apexcharts": "^3.50.0", "awesomplete": "^1.1.5", "bootstrap-cookie-alert": "^1.2.1", "chart.js": "^2.9.4", @@ -26,19 +26,20 @@ }, "devDependencies": { "@popperjs/core": "^2.11", - "@vitejs/plugin-vue": "^4.3.3", + "@types/luxon": "^3.4.2", + "@vitejs/plugin-vue": "^5.0.5", "bootstrap": "^5.3", "jquery": "^3.7.0", - "laravel-vite-plugin": "^1.0.0", + "laravel-vite-plugin": "^1.0.5", "lodash": "^4.17.21", - "sass": "^1.74.1", - "vite": "^5.2.8" + "sass": "^1.77.8", + "vite": "^5.3.3" } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "bin": { "parser": "bin/babel-parser.js" }, @@ -415,18 +416,17 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", - "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==", - "hasInstallScript": true, + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", "engines": { "node": ">=6" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@popperjs/core": { "version": "2.11.8", @@ -439,9 +439,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz", + "integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==", "cpu": [ "arm" ], @@ -452,9 +452,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz", + "integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==", "cpu": [ "arm64" ], @@ -465,9 +465,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz", + "integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==", "cpu": [ "arm64" ], @@ -478,9 +478,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz", + "integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==", "cpu": [ "x64" ], @@ -491,9 +491,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz", + "integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==", "cpu": [ "arm" ], @@ -504,9 +504,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz", + "integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==", "cpu": [ "arm" ], @@ -517,9 +517,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz", + "integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz", + "integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==", "cpu": [ "arm64" ], @@ -543,9 +543,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz", + "integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==", "cpu": [ "ppc64" ], @@ -556,9 +556,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz", + "integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==", "cpu": [ "riscv64" ], @@ -569,9 +569,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz", + "integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==", "cpu": [ "s390x" ], @@ -582,9 +582,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz", + "integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==", "cpu": [ "x64" ], @@ -595,9 +595,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz", + "integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==", "cpu": [ "x64" ], @@ -608,9 +608,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz", + "integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==", "cpu": [ "arm64" ], @@ -621,9 +621,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz", + "integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==", "cpu": [ "ia32" ], @@ -634,9 +634,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz", + "integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==", "cpu": [ "x64" ], @@ -652,63 +652,69 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz", - "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", "dev": true, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^4.0.0 || ^5.0.0", + "vite": "^5.0.0", "vue": "^3.2.25" } }, "node_modules/@vue/compiler-core": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.31.tgz", - "integrity": "sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.34.tgz", + "integrity": "sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==", "dependencies": { "@babel/parser": "^7.24.7", - "@vue/shared": "3.4.31", + "@vue/shared": "3.4.34", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz", - "integrity": "sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.34.tgz", + "integrity": "sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==", "dependencies": { - "@vue/compiler-core": "3.4.31", - "@vue/shared": "3.4.31" + "@vue/compiler-core": "3.4.34", + "@vue/shared": "3.4.34" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz", - "integrity": "sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.34.tgz", + "integrity": "sha512-x6lm0UrM03jjDXTPZgD9Ad8bIVD1ifWNit2EaWQIZB5CULr46+FbLQ5RpK7AXtDHGjx9rmvC7QRCTjsiGkAwRw==", "dependencies": { "@babel/parser": "^7.24.7", - "@vue/compiler-core": "3.4.31", - "@vue/compiler-dom": "3.4.31", - "@vue/compiler-ssr": "3.4.31", - "@vue/shared": "3.4.31", + "@vue/compiler-core": "3.4.34", + "@vue/compiler-dom": "3.4.34", + "@vue/compiler-ssr": "3.4.34", + "@vue/shared": "3.4.34", "estree-walker": "^2.0.2", "magic-string": "^0.30.10", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz", - "integrity": "sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.34.tgz", + "integrity": "sha512-8TDBcLaTrFm5rnF+Qm4BlliaopJgqJ28Nsrc80qazynm5aJO+Emu7y0RWw34L8dNnTRdcVBpWzJxhGYzsoVu4g==", "dependencies": { - "@vue/compiler-dom": "3.4.31", - "@vue/shared": "3.4.31" + "@vue/compiler-dom": "3.4.34", + "@vue/shared": "3.4.34" } }, "node_modules/@vue/devtools-api": { @@ -717,57 +723,60 @@ "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==" }, "node_modules/@vue/reactivity": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.31.tgz", - "integrity": "sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.34.tgz", + "integrity": "sha512-ua+Lo+wBRlBEX9TtgPOShE2JwIO7p6BTZ7t1KZVPoaBRfqbC7N3c8Mpzicx173fXxx5VXeU6ykiHo7WgLzJQDA==", "dependencies": { - "@vue/shared": "3.4.31" + "@vue/shared": "3.4.34" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.31.tgz", - "integrity": "sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.34.tgz", + "integrity": "sha512-PXhkiRPwcPGJ1BnyBZFI96GfInCVskd0HPNIAZn7i3YOmLbtbTZpB7/kDTwC1W7IqdGPkTVC63IS7J2nZs4Ebg==", "dependencies": { - "@vue/reactivity": "3.4.31", - "@vue/shared": "3.4.31" + "@vue/reactivity": "3.4.34", + "@vue/shared": "3.4.34" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz", - "integrity": "sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.34.tgz", + "integrity": "sha512-dXqIe+RqFAK2Euak4UsvbIupalrhc67OuQKpD7HJ3W2fv8jlqvI7szfBCsAEcE8o/wyNpkloxB6J8viuF/E3gw==", "dependencies": { - "@vue/reactivity": "3.4.31", - "@vue/runtime-core": "3.4.31", - "@vue/shared": "3.4.31", + "@vue/reactivity": "3.4.34", + "@vue/runtime-core": "3.4.34", + "@vue/shared": "3.4.34", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.31.tgz", - "integrity": "sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.34.tgz", + "integrity": "sha512-GeyEUfMVRZMD/mZcNONEqg7MiU10QQ1DB3O/Qr6+8uXpbwdlmVgQ5Qs1/ZUAFX1X2UUtqMoGrDRbxdWfOJFT7Q==", "dependencies": { - "@vue/compiler-ssr": "3.4.31", - "@vue/shared": "3.4.31" + "@vue/compiler-ssr": "3.4.34", + "@vue/shared": "3.4.34" }, "peerDependencies": { - "vue": "3.4.31" + "vue": "3.4.34" } }, "node_modules/@vue/shared": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.31.tgz", - "integrity": "sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==" + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.34.tgz", + "integrity": "sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A==" }, "node_modules/@vuepic/vue-datepicker": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-8.8.1.tgz", - "integrity": "sha512-8ehfUz1m69Vuc16Pm4ukgb3Mg1VT14x4EsG1ag4O/qbSNRWztTo+pUV4JnFt0FGLl5gGb6NXlxIvR7EjLgD7Gg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-9.0.1.tgz", + "integrity": "sha512-5sSdwib5cY8cE4Y7SCh+Zemfp+U/m6BDcgaPwd5Vmdv5LAASyV0wugn9sTb6NWX0sIQEdrGDl/RmD9EjcIke3A==", "dependencies": { "date-fns": "^3.6.0" }, + "engines": { + "node": ">=18.12.0" + }, "peerDependencies": { "vue": ">=3.2.0" } @@ -791,9 +800,9 @@ } }, "node_modules/apexcharts": { - "version": "3.49.2", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.2.tgz", - "integrity": "sha512-vBB8KgwfD9rSObA7s4kY2rU6DeaN67gTR3JN7r32ztgKVf8lKkdFQ6iUhk6oIHrV7W8PoHhr5EwKymn0z5Fz6A==", + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.51.0.tgz", + "integrity": "sha512-WpCdVdGiJjf9SAyEeg2rl3q5OqCcNqiEmH0+filMraUiH6Vqyn5GFeMMyH0pon44xjNr1G0xzIRERKRmsGEuRA==", "dependencies": { "@yr/monotone-cubic-spline": "^1.0.3", "svg.draggable.js": "^2.2.2", @@ -805,9 +814,9 @@ } }, "node_modules/awesomplete": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/awesomplete/-/awesomplete-1.1.5.tgz", - "integrity": "sha512-UFw1mPW8NaSECDSTC36HbAOTpF9JK2wBUJcNn4MSvlNtK7SZ9N72gB+ajHtA6D1abYXRcszZnBA4nHBwvFwzHw==" + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/awesomplete/-/awesomplete-1.1.7.tgz", + "integrity": "sha512-hwClzFReIIfheoMWpoJ7juLp1o6Q7YjTtEdZIGfXvhRHA9ewKM03VS4ZoK+OEFIKM066PfQt9pzRIGV1TguRBw==" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -1037,9 +1046,9 @@ } }, "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", "dev": true }, "node_modules/is-binary-path": { @@ -1091,9 +1100,9 @@ "dev": true }, "node_modules/laravel-vite-plugin": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.4.tgz", - "integrity": "sha512-dEj8Q/Fsn0kKbOQ55bl/NmyJL+dD6OxnVaM/nNByw5XV4b00ky6FzXKVuHLDr4BvSJKH1y6oaOcEG5wKpCZ5+A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.5.tgz", + "integrity": "sha512-Zv+to82YLBknDCZ6g3iwOv9wZ7f6EWStb9pjSm7MGe9Mfoy5ynT2ssZbGsMr1udU6rDg9HOoYEVGw5Qf+p9zbw==", "dev": true, "dependencies": { "picocolors": "^1.0.0", @@ -1255,9 +1264,9 @@ } }, "node_modules/pinia/node_modules/vue-demi": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", - "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -1280,9 +1289,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.40", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", + "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", "funding": [ { "type": "opencollective", @@ -1319,9 +1328,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.0.tgz", + "integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -1334,29 +1343,29 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.19.0", + "@rollup/rollup-android-arm64": "4.19.0", + "@rollup/rollup-darwin-arm64": "4.19.0", + "@rollup/rollup-darwin-x64": "4.19.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.19.0", + "@rollup/rollup-linux-arm-musleabihf": "4.19.0", + "@rollup/rollup-linux-arm64-gnu": "4.19.0", + "@rollup/rollup-linux-arm64-musl": "4.19.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0", + "@rollup/rollup-linux-riscv64-gnu": "4.19.0", + "@rollup/rollup-linux-s390x-gnu": "4.19.0", + "@rollup/rollup-linux-x64-gnu": "4.19.0", + "@rollup/rollup-linux-x64-musl": "4.19.0", + "@rollup/rollup-win32-arm64-msvc": "4.19.0", + "@rollup/rollup-win32-ia32-msvc": "4.19.0", + "@rollup/rollup-win32-x64-msvc": "4.19.0", "fsevents": "~2.3.2" } }, "node_modules/sass": { - "version": "1.77.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", - "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -1474,13 +1483,13 @@ } }, "node_modules/vite": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", - "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", + "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", "dev": true, "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "rollup": "^4.13.0" }, "bin": { @@ -1529,9 +1538,9 @@ } }, "node_modules/vite-plugin-full-reload": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz", - "integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", "dev": true, "dependencies": { "picocolors": "^1.0.0", @@ -1539,15 +1548,15 @@ } }, "node_modules/vue": { - "version": "3.4.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.31.tgz", - "integrity": "sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.34.tgz", + "integrity": "sha512-VZze05HWlA3ItreQ/ka7Sx7PoD0/3St8FEiSlSTVgb6l4hL+RjtP2/8g5WQBzZgyf8WG2f+g1bXzC7zggLhAJA==", "dependencies": { - "@vue/compiler-dom": "3.4.31", - "@vue/compiler-sfc": "3.4.31", - "@vue/runtime-dom": "3.4.31", - "@vue/server-renderer": "3.4.31", - "@vue/shared": "3.4.31" + "@vue/compiler-dom": "3.4.34", + "@vue/compiler-sfc": "3.4.34", + "@vue/runtime-dom": "3.4.34", + "@vue/server-renderer": "3.4.34", + "@vue/shared": "3.4.34" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index b3ae7f575..7418ae79d 100644 --- a/package.json +++ b/package.json @@ -7,18 +7,19 @@ }, "devDependencies": { "@popperjs/core": "^2.11", - "@vitejs/plugin-vue": "^4.3.3", + "@types/luxon": "^3.4.2", + "@vitejs/plugin-vue": "^5.0.5", "bootstrap": "^5.3", "jquery": "^3.7.0", - "laravel-vite-plugin": "^1.0.0", + "laravel-vite-plugin": "^1.0.5", "lodash": "^4.17.21", - "sass": "^1.74.1", - "vite": "^5.2.8" + "sass": "^1.77.8", + "vite": "^5.3.3" }, "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", - "@vuepic/vue-datepicker": "^8.4.0", - "apexcharts": "^3.46.0", + "@vuepic/vue-datepicker": "^9.0.1", + "apexcharts": "^3.50.0", "awesomplete": "^1.1.5", "bootstrap-cookie-alert": "^1.2.1", "chart.js": "^2.9.4", diff --git a/public/uploads/avatars/stock_enno.png b/public/uploads/avatars/stock_enno.png deleted file mode 100644 index f0e165ca3..000000000 Binary files a/public/uploads/avatars/stock_enno.png and /dev/null differ diff --git a/resources/sass/app-dark.scss b/resources/sass/app-dark.scss index b1cfbf58b..b33f607e4 100644 --- a/resources/sass/app-dark.scss +++ b/resources/sass/app-dark.scss @@ -29,6 +29,10 @@ background-color: $dm-base-5; } + .hover-card { + background-color: $dm-base; + } + .card-header { border-bottom-color: $dm-base-20; } diff --git a/resources/sass/app.scss b/resources/sass/app.scss index 32d2089dc..e55ef4713 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -19,6 +19,10 @@ body { box-shadow: 0 10px 20px 0 rgba(0, 0, 0, 0.1); } +.hover-card { + background-color: $light; +} + @import "notyf/notyf.min.css"; // Overall site layout diff --git a/resources/types/Api.ts b/resources/types/Api.ts new file mode 100644 index 000000000..b9a10f38b --- /dev/null +++ b/resources/types/Api.ts @@ -0,0 +1,3636 @@ +/* eslint-disable */ +/* tslint:disable */ + +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** + * Coordinate + * GeoJson Coordinates + */ +export interface Coordinate { + /** + * @format float + * @example "Feature" + */ + type?: number; + /** @example "{}" */ + properties?: object; + geometry?: { + /** @example "Point" */ + type?: string; + coordinates?: any[]; + }; +} + +/** + * FeatureCollection + * featurecollection of multiple GeoJson points + */ +export interface FeatureCollection { + /** + * type + * @example "FeatureCollection" + */ + type?: string; + features?: Coordinate[]; +} + +/** + * LivePointDto + * All necessary information to calculate live position + */ +export interface LivePointDto { + /** GeoJson Coordinates */ + point?: Coordinate; + /** featurecollection of multiple GeoJson points */ + polyline?: FeatureCollection; + /** + * arrival + * arrival at end of polyline in UNIX time format + * @format integer + * @example 1692538680 + */ + arrival?: number; + /** + * departure + * departure at start of polyline in UNIX time format + * @format integer + * @example 1692538740 + */ + departure?: number; + /** + * lineName + * name of line + * @format string + * @example "ICE 123" + */ + lineName?: string; + /** + * statusId + * ID of status + * @deprecated + * @format int + * @example 12345 + */ + statusId?: number; +} + +/** + * Mention + * Mentioned user and position in status body + */ +export interface MentionDto { + /** User model */ + user?: User; + /** + * position + * @format int + * @example 0 + */ + position?: number; + /** + * length + * @format integer + * @example 4 + */ + length?: number; +} + +/** + * Station + * train station model + */ +export interface Station { + /** + * id + * id + * @example "4711" + */ + id?: number; + /** + * name + * name of the station + * @example "Karlsruhe Hbf" + */ + name?: string; + /** + * latitude + * latitude of the station + * @format float + * @example "48.991591" + */ + latitude?: number; + /** + * longitude + * longitude of the station + * @format float + * @example "8.400538" + */ + longitude?: number; + /** + * ibnr + * IBNR of the station + * @example "8000191" + */ + ibnr?: number; + /** + * rilIdentifier + * Identifier specified in 'Richtline 100' of the Deutsche Bahn + * @example "RK" + */ + rilIdentifier?: string | null; +} + +/** + * Business + * What type of travel (0=private, 1=business, 2=commute) did the user specify? + * @example 0 + */ +export enum Business { + Value0 = 0, + Value1 = 1, + Value2 = 2, +} + +/** + * category + * Category of transport. + * @example "suburban" + */ +export enum HafasTravelType { + NationalExpress = "nationalExpress", + National = "national", + RegionalExp = "regionalExp", + Regional = "regional", + Suburban = "suburban", + Bus = "bus", + Ferry = "ferry", + Subway = "subway", + Tram = "tram", + Taxi = "taxi", + Plane = "plane", +} + +/** + * MapProvider + * What type of map provider (cargo, open-railway-map) did the user specify? + * @example "cargo" + */ +export enum MapProvider { + Cargo = "cargo", + OpenRailwayMap = "open-railway-map", +} + +/** + * visibility + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private) did the user specify for + * * future posts to Mastodon? Some instances such as chaos.social discourage bot posts on public timelines. + * @example 1 + */ +export enum MastodonVisibility { + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3, +} + +/** + * PointsReason + * What is the reason for the points calculation factor? (0=in time => 100%, 1=good enough => 25%, 2=not sufficient (1 point), 3=forced => no points) + * @example 1 + */ +export enum PointReason { + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3, + Value4 = 4, + Value5 = 5, +} + +/** + * visibility + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + * @example 0 + */ +export enum StatusVisibility { + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3, + Value4 = 4, +} + +/** + * travelType + * @example "suburban" + */ +export enum TravelType { + Express = "express", + Regional = "regional", + Suburban = "suburban", + Bus = "bus", + Ferry = "ferry", + Subway = "subway", + Tram = "tram", + Taxi = "taxi", + Plane = "plane", +} + +/** Client */ +export interface ClientResource { + /** + * Model -> OAuthClient + * @example 1 + */ + id?: number; + /** @example "Träwelling App" */ + name?: string; + /** @example "https://traewelling.de/privacy-policy" */ + privacyPolicyUrl?: string; +} + +/** EventDetails */ +export interface EventDetailsResource { + /** @example 39 */ + id?: number; + /** @example "9_euro_ticket" */ + slug?: string; + /** @example 12345 */ + trainDistance?: number; + /** @example 12345 */ + trainDuration?: number; +} + +/** Event */ +export interface EventResource { + /** @example 39 */ + id?: number; + /** @example "9-Euro-Ticket" */ + name?: string; + /** @example "9_euro_ticket" */ + slug?: string; + /** @example "NeunEuroTicket" */ + hashtag?: string; + /** @example "9-Euro-Ticket GmbH" */ + host?: string; + /** @example "https://9-euro-ticket.de" */ + url?: string; + /** + * @format date-time + * @example "2022-01-01T00:00:00+00:00" + */ + begin?: string; + /** + * @format date-time + * @example "2022-01-02T00:00:00+00:00" + */ + end?: string; + /** train station model */ + station?: Station; +} + +/** LeaderboardUserResource */ +export interface LeaderboardUserResource { + /** + * username of user + * @example "Gertrud123" + */ + username?: string; + /** + * URL of the profile picture of the user + * @example "https://traewelling.de/@Gertrud123/picture" + */ + profilePicture?: string; + /** + * duration travelled in minutes + * @example 6 + */ + totalDuration?: number; + /** + * distance travelled in meters + * @example 12345 + */ + totalDistance?: number; + /** points of user */ + points?: number; +} + +/** + * LightUser + * User model with just basic information + */ +export interface LightUserResource { + /** @example 1 */ + id?: number; + /** @example "Gertrud" */ + displayName?: string; + /** @example "Gertrud123" */ + username?: string; + /** @example "https://traewelling.de/@Gertrud123/picture" */ + profilePicture?: string; + /** @example "https://traewelling.social/@Gertrud123" */ + mastodonUrl?: string; + /** @example false */ + preventIndex?: boolean; +} + +export interface OperatorResource { + /** @example 1 */ + id?: number; + /** @example "db-regio-ag-nord" */ + identifier?: string; + /** @example "DB Regio AG Nord" */ + name?: string; +} + +/** Station */ +export interface StationResource { + /** @example "1" */ + id?: number; + /** @example "Karlsruhe Hbf" */ + name?: string; + /** @example "48.993207" */ + latitude?: number; + /** @example "8.400977" */ + longitude?: number; + /** @example "8000191" */ + ibnr?: string; + /** @example "RK" */ + rilIdentifier?: string; +} + +/** Status */ +export interface StatusResource { + /** @example 12345 */ + id?: number; + /** + * User defined status text + * @example "Hello world!" + */ + body?: any; + /** Mentions in the status body */ + bodyMentions?: MentionDto[]; + /** What type of travel (0=private, 1=business, 2=commute) did the user specify? */ + business?: Business; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + visibility?: StatusVisibility; + /** + * How many people have liked this status + * @example 12 + */ + likes?: number; + /** + * Did the currently authenticated user like this status? (if unauthenticated = false) + * @example true + */ + liked?: boolean; + /** + * Do the author of this status and the currently authenticated user allow liking of statuses? Only show the like UI if set to true + * @example true + */ + isLikable?: boolean; + client?: ClientResource; + /** + * creation date of this status + * @format datetime + * @example "2022-07-17T13:37:00+02:00" + */ + createdAt?: string; + train?: TransportResource; + event?: EventResource | null; + /** User model with just basic information */ + userDetails?: LightUserResource; + tags?: StatusTagResource[]; +} + +/** StatusTagResource */ +export interface StatusTagResource { + /** @example "trwl:vehicle_number" */ + key?: string; + /** @example "94 80 0450 921 D-AVG" */ + value?: string; + /** @example "1" */ + visibility?: number; +} + +/** StopoverResource */ +export interface StopoverResource { + /** @example 12345 */ + id?: number; + /** + * name of the station + * @example "Karlsruhe Hbf" + */ + name?: string; + /** + * Identifier specified in 'Richtline 100' of the Deutsche Bahn + * @example "RK" + */ + rilIdentifier?: string | null; + /** + * IBNR identifier of Deutsche Bahn + * @example "8000191" + */ + evaIdentifier?: string | null; + /** + * currently known arrival time. Equal to arrivalReal if known. Else equal to arrivalPlanned. + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + arrival?: string | null; + /** + * planned arrival according to timetable records + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + arrivalPlanned?: string | null; + /** + * real arrival according to live data + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + arrivalReal?: string | null; + /** + * planned arrival platform according to timetable records + * @example "5" + */ + arrivalPlatformPlanned?: string | null; + /** + * real arrival platform according to live data + * @example "5 A-F" + */ + arrivalPlatformReal?: string | null; + /** + * currently known departure time. Equal to departureReal if known. Else equal to departurePlanned. + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + departure?: string | null; + /** + * planned departure according to timetable records + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + departurePlanned?: string | null; + /** + * real departure according to live data + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + departureReal?: string | null; + /** + * planned departure platform according to timetable records + * @example "5" + */ + departurePlatformPlanned?: string | null; + /** + * real departure platform according to live data + * @example "5 A-F" + */ + departurePlatformReal?: string | null; + /** @example "5 A-F" */ + platform?: string | null; + /** + * Is there a delay in the arrival time? + * @example false + */ + isArrivalDelayed?: boolean; + /** + * Is there a delay in the departure time? + * @example false + */ + isDepartureDelayed?: boolean; + /** + * is this stopover cancelled? + * @example false + */ + cancelled?: boolean; +} + +/** TransportResource */ +export interface TransportResource { + /** @example "4711" */ + trip?: number; + /** @example "1|1234|567" */ + hafasId?: string; + /** Category of transport. */ + category?: HafasTravelType; + /** + * Internal number of the journey + * @example "4-a6s8-8" + */ + number?: any; + /** @example "S 1" */ + lineName?: string; + /** @example 85639 */ + journeyNumber?: number; + /** + * Distance in meters + * @example 10000 + */ + distance?: number; + /** @example 37 */ + points?: number; + /** + * Duration in minutes + * @example 30 + */ + duration?: number; + /** + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + manualDeparture?: string | null; + /** + * @format date-time + * @example "2022-07-17T13:37:00+02:00" + */ + manualArrival?: string | null; + origin?: StopoverResource; + destination?: StopoverResource; + operator?: OperatorResource; +} + +/** UserAuth */ +export interface UserAuthResource { + /** @example "1" */ + id?: number; + /** @example "Gertrud" */ + displayName?: string; + /** @example "Gertrud123" */ + username?: string; + /** @example "https://traewelling.de/@Gertrud123/picture" */ + profilePicture?: string; + /** @example "100" */ + totalDistance?: number; + /** @example "100" */ + totalDuration?: number; + /** @example "100" */ + points?: number; + /** @example "https://mastodon.social/@Gertrud123" */ + mastodonUrl?: string | null; + /** @example "false" */ + privateProfile?: boolean; + /** @example "false" */ + preventIndex?: boolean; + /** @example "true" */ + likes_enabled?: boolean; + home?: StationResource; + /** @example "de" */ + language?: string; + /** @example 0 */ + defaultStatusVisibility?: number; + /** @example ["admin","open-beta","closed-beta"] */ + roles?: string[]; +} + +/** BearerTokenResponse */ +export interface BearerTokenResponse { + /** + * token + * Bearer Token. Use in Authentication-Header with prefix 'Bearer '. (space is needed) + * @example "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiZWU2ZWZiOWUxYTIwN2FmMjZjNjk4NjVkOTA5ODNmNzFjYzYyMzE5ODA3NGU1NjlhNjU1MGRiMTdhMWY5YmNhMmY4ZjNjNTQ4ZGZkZTY5ZmUiLCJpYXQiOjE2NjYxODUzMDYuOTczODU3LCJuYmYiOjE2NjYxODUzMDYuOTczODYsImV4cCI6MTY5NzcyMTMwNi45NDYyNDgsInN1YiI6IjEiLCJzY29wZXMiOltdfQ.tiv8VeL8qw6BRwo5QZZ71Zn3WnFJjtvVciahiUJjzVNfqgofdRF6EoWrTFc_WmrgbVCdfXBjBI02fjbSrsD4....." + */ + token?: string; + /** + * slug + * end of life for this token. Lifespan is usually one year. + * @example "2023-10-19T15:15:06+02:00" + */ + expires_at?: string; +} + +/** + * CheckinRequestBody + * Fields for creating a train checkin + */ +export interface CheckinRequestBody { + /** + * body + * Text that should be added to the post + * @maxLength 280 + * @example "Meine erste Fahrt nach Knuffingen!" + */ + body?: string | null; + /** What type of travel (0=private, 1=business, 2=commute) did the user specify? */ + business?: Business; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + visibility?: StatusVisibility; + /** + * eventId + * Id of an event the status should be connected to + */ + eventId?: number | null; + /** + * toot + * Should this status be posted to mastodon? + * @example "false" + */ + toot?: boolean | null; + /** + * chainPost + * Should this status be posted to mastodon as a chained post? + * @example "false" + */ + chainPost?: boolean | null; + /** + * ibnr + * If true, the `start` and `destination` properties can be supplied as an ibnr. Otherwise they + * * should be given as the Träwelling-ID. Default behavior is `false`. + * @example "true" + */ + ibnr?: boolean | null; + /** + * tripId + * The HAFAS tripId for the to be checked in train + * @example "1|323306|1|80|17072022" + */ + tripId?: any; + /** + * lineName + * The line name for the to be checked in train + * @example "S 4" + */ + lineName?: any; + /** + * start + * The Station-ID of the starting point (see `ibnr`) + * @example "8000191" + */ + start?: number; + /** + * destination + * The Station-ID of the destination (see `ibnr`) + * @example "8079045" + */ + destination?: number; + /** + * departure + * Timestamp of the departure + * @example "2022-12-19T20:41:00+01:00" + */ + departure?: any; + /** + * arrival + * Timestamp of the arrival + * @example "2022-12-19T20:42:00+01:00" + */ + arrival?: any; + /** + * force + * If true, the checkin will be created, even if a colliding checkin exists. No points will be + * * awarded. + * @example "false" + */ + force?: boolean | null; +} + +/** CheckinResponse */ +export interface CheckinResponse { + /** + * status + * StatusModel of the created status + * @example "" + */ + status?: StatusResource; + /** Points model */ + points?: Points; + /** + * alsoOnThisconnection + * Statuses of other people on this connection + */ + alsoOnThisConnection?: StatusResource[]; +} + +/** + * EventSuggestion + * Fields for suggesting an event + */ +export interface EventSuggestion { + /** + * name + * name of the event + * @maxLength 255 + * @example "Eröffnung der Nebenbahn in Knuffingen" + */ + name?: string; + /** + * host + * host of the event + * @example "MiWuLa" + */ + host?: string | null; + /** + * begin + * Timestamp for the start of the event + * @example "2022-06-01T00:00:00+02:00" + */ + begin?: any; + /** + * end + * Timestamp for the end of the event + * @example "2022-08-31T23:59:00+02:00" + */ + end?: any; + /** + * url + * external URL for this event + * @maxLength 255 + * @example "https://www.bundesregierung.de/breg-de/aktuelles/faq-9-euro-ticket-2028756" + */ + url?: string | null; + /** + * hashtag + * hashtag for this event + * @maxLength 40 + * @example "gpn21" + */ + hashtag?: string | null; + /** + * nearestStation + * Query string for the nearest station to this event + * @maxLength 255 + * @example "Berlin Hbf" + */ + nearestStation?: string | null; +} + +/** + * Links + * pagination links + */ +export interface Links { + /** + * first + * URL to first page of this pagination + * @format uri + * @example "https://traewelling.de/api/v1/ENDPOINT?page=1" + */ + first?: string | null; + /** + * last + * URL to last page of this pagination (mostly null) + * @format uri + * @example null + */ + last?: string | null; + /** + * prev + * URL to previous page of this pagination (mostly null) + * @format uri + * @example "https://traewelling.de/api/v1/ENDPOINT?page=1" + */ + prev?: string | null; + /** + * next + * URL to next page of this pagination (mostly null) + * @format uri + * @example "https://traewelling.de/api/v1/ENDPOINT?page=2" + */ + next?: string | null; +} + +/** + * Meta + * Pagination meta data + */ +export interface PaginationMeta { + /** + * current_page + * currently displayed page in this pagination + * @example 2 + */ + current_page?: number; + /** + * from + * The first element on this page is the nth element of the query + * @example 16 + */ + from?: number; + /** + * path + * The path of this pagination + * @format url + * @example "https://traewelling.de/api/v1/ENDPOINT" + */ + path?: string; + /** + * per_page + * the amount of items per page in this pagination + * @example 15 + */ + per_page?: number; + /** + * to + * The last element on this page is the nth element of the query + * @example 30 + */ + to?: number; +} + +/** LikeResponse */ +export interface LikeResponse { + /** + * count + * Amount of likes + * @format int32 + * @example 12 + */ + count?: number; +} + +/** + * Notification + * Notification model + */ +export interface Notification { + /** + * ID + * ID + * @format string + * @example "bb1ba9a5-9c2b-43c3-b8c9-2f70651fc51c" + */ + id?: string; + /** + * type + * type of notification + * @example "UserJoinedConnection" + */ + type?: string; + /** + * leadFormatted + * the title of notification in html formatted form + * @format string + * @example "@bob is in your connection!" + */ + leadFormatted?: string; + /** + * lead + * the title of notification in plain text form + * @format string + * @example "@bob is in your connection!" + */ + lead?: string; + /** + * noticeFormatted + * the body of notification in html formatted form + * @format string + * @example "@bob is on S 81 from Karlsruhe Hbf to Freudenstadt Hbf." + */ + noticeFormatted?: string; + /** + * notice + * the body of notification in plain text form + * @format string + * @example "@bob is on S 81 from Karlsruhe Hbf to Freudenstadt Hbf." + */ + notice?: string; + /** + * link + * the link to the notification + * @format string + * @example "https://traewelling.de/status/123456" + */ + link?: string; + /** + * data + * the data of the notification + */ + data?: any[]; + /** + * readAt + * the date when the notification was read, null if not read yet + * @format string + * @example "2023-01-01T00:00:00+00:00" + */ + readAt?: string | null; + /** + * createdAt + * the date when the notification was created + * @format string + * @example "2023-01-01T00:00:00+00:00" + */ + createdAt?: string; + /** + * createdAtForHumans + * DON'T USE THIS ATTRIBUTE! This Attribute will be removed in the future. The date when the notification was created, but in human readable form + * @format string + * @example "2 days ago" + */ + createdAtForHumans?: string; +} + +/** + * PaginationPage + * pagination links + */ +export type PaginationPage = any; + +/** + * Points + * Points model + */ +export interface Points { + /** + * points + * points + * @format int + * @example 1 + */ + points?: number; + calculation?: PointsCalculation; + /** + * additional + * extra points that can be given + */ + additional?: any[]; +} + +/** PointsCalculation */ +export interface PointsCalculation { + /** + * base + * Basepoints for this type of vehicle + * @format float + * @example 0.5 + */ + base?: number; + /** + * distance + * Points for the travelled distance + * @example 0.25 + */ + distance?: any; + /** + * factor + * @example 0.25 + */ + factor?: any; + /** What is the reason for the points calculation factor? (0=in time => 100%, 1=good enough => 25%, 2=not sufficient (1 point), 3=forced => no points) */ + reason?: PointReason; +} + +/** + * Polyline + * Polyline of a single status as GeoJSON Feature + */ +export interface Polyline { + /** + * type + * @example "Feature" + */ + type?: string; + geometry?: { + /** @example "LineString" */ + type?: object; + coordinates?: any[]; + }; + properties?: { + /** @example 1337 */ + statusId?: number; + }; +} + +/** + * StatusTag + * StatusTag model + */ +export interface StatusTag { + /** + * key + * Key of tag + * @example "trwl:ticket" + */ + key?: string; + /** + * value + * Value of tag + * @example "BahnCard 100" + */ + value?: string; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + visibility?: StatusVisibility; +} + +/** + * SuccessResponse + * Success Response + */ +export interface SuccessResponse { + /** + * status + * status + * @example "success" + */ + status?: string; +} + +/** + * User + * User model + */ +export interface User { + /** + * ID + * ID + * @format int + * @example 1 + */ + id?: number; + /** + * displayName + * Display name of the user + * @example "Gertrud" + */ + displayName?: any; + /** + * username + * username of user + * @example "Gertrud123" + */ + username?: string; + /** + * profilePicture + * URL of the profile picture of the user + * @example "https://traewelling.de/@Gertrud123/picture" + */ + profilePicture?: number; + /** + * trainDistance + * distance travelled by train in meters + * @format int + * @example 12345 + */ + trainDistance?: number; + /** + * trainDuration + * duration travelled by train in minutes + * @format int + * @example 6 + */ + trainDuration?: number; + /** + * points + * Current points of the last 7 days + * @format int + * @example 300 + */ + points?: number; + /** + * mastodonUrl + * URL to the Mastodon profile of the user + * @example "https://chaos.social/@traewelling" + */ + mastodonUrl?: string | null; + /** + * privateProfile + * is this profile set to private? + * @example false + */ + privateProfile?: boolean; + /** + * likes_enabled + * Does this profile allow likes? Only offer the UI to like any status if this setting is set to true. If set to false, the likes API will return 403. + * @example true + */ + likes_enabled?: boolean; + /** + * userInvisibleToMe + * Can the currently authenticated user see the statuses of this user? + * @example false + */ + userInvisibleToMe?: boolean; + /** + * muted + * Is this user muted by the currently authenticated user? + * @example false + */ + muted?: boolean; + /** + * following + * Does the currently authenticated user follow this user? + * @example false + */ + following?: boolean; + /** + * followPending + * Is there a currently pending follow request? + * @example false + */ + followPending?: boolean; + /** + * preventIndex + * Did the user choose to prevent search engines from indexing their profile? + * @example false + */ + preventIndex?: boolean; +} + +/** + * UserProfileSettings + * Model for all user profile settings + */ +export interface UserProfileSettings { + /** + * username + * username + * @example "Gertrud123" + */ + username?: string; + /** + * displayName + * Display name of the user + * @example "Gertrud" + */ + displayName?: any; + /** + * profilePicture + * URL of the profile picture of the user + * @example "https://traewelling.de/@Gertrud123/picture" + */ + profilePicture?: number; + /** + * privateProfile + * Is the profile private? + * @format boolean + * @example false + */ + privateProfile?: boolean; + /** + * preventIndex + * Did the user choose to prevent search engines from indexing their profile? + * @format boolean + * @example false + */ + preventIndex?: boolean; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + defaultStatusVisibility?: StatusVisibility; + /** + * privacyHideDays + * Number of days after which a status is hidden from the public + * @format int + * @example 1 + */ + privacyHideDays?: any; + /** + * password + * Does the user have a password set? + * @format boolean + * @example true + */ + password?: boolean; + /** + * email + * The email address of the user + * @example "gertrud@example.com" + */ + email?: string | null; + /** + * emailVerified + * Is the email address verified? + * @format boolean + * @example "https://chaos.social/@traewelling" + */ + emailVerified?: boolean; + /** + * profilePictureSet + * Has the user set a profile picture other then the default one? + * @example false + */ + profilePictureSet?: boolean; + /** + * mastodon + * Mastodon URL of user + * @example "https://chaos.social/@traewelling" + */ + mastodon?: string; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + mastodonVisibility?: StatusVisibility; +} + +/** + * Webhook + * Webhook model + */ +export interface Webhook { + /** + * ID + * ID + * @format int + * @example 12345 + */ + id?: number; + /** + * ClientID + * ID of the client which created this webhook + * @format int + * @example 12345 + */ + clientId?: number; + /** + * UserID + * ID of the user which created this webhook + * @format int + * @example 12345 + */ + userId?: number; + /** + * url + * URL where the webhook gets sent to + * @example "https://example.com/webhook" + */ + url?: any; + /** + * createdAt + * creation date of this webhook + * @format datetime + * @example "2022-07-17T13:37:00+02:00" + */ + createdAt?: string; + /** + * events + * array of events this webhook receives + */ + events?: any[]; +} + +/** + * StatusUpdateBody + * Status Update Body + */ +export interface StatusUpdateBody { + /** + * Status-Text to be displayed alongside the checkin + * @maxLength 280 + * @example "Wow. This train is extremely crowded!" + */ + body?: any; + /** What type of travel (0=private, 1=business, 2=commute) did the user specify? */ + business?: Business; + /** + * What type of visibility (0=public, 1=unlisted, 2=followers, 3=private, 4=authenticated) did the + * * user specify? + */ + visibility?: StatusVisibility; + /** + * The ID of the event this status is related to - or null + * @example "1" + */ + eventId?: any; + /** + * Manual departure time set by the user + * @format date + * @example "2020-01-01 12:00:00" + */ + manualDeparture?: any; + /** + * Manual arrival time set by the user + * @format date + * @example "2020-01-01 13:00:00" + */ + manualArrival?: any; + /** + * Destination station id + * @example "1" + */ + destinationId?: any; + /** + * Destination arrival time + * @format date + * @example "2020-01-01 13:00:00" + */ + destinationArrivalPlanned?: any; +} + +export type QueryParamsType = Record; +export type ResponseFormat = keyof Omit; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; +} + +export type RequestParams = Omit; + +export interface ApiConfig { + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; + customFetch?: typeof fetch; +} + +export interface HttpResponse extends Response { + data: D; + error: E; +} + +type CancelToken = Symbol | string | number; + +export enum ContentType { + Json = "application/json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", +} + +export class HttpClient { + public baseUrl: string = "https://traewelling.de/api/v1"; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig["securityWorker"]; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: "same-origin", + headers: {}, + redirect: "follow", + referrerPolicy: "no-referrer", + }; + + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; + } + + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); + } + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); + return keys + .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) + .join("&"); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, + [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), + [ContentType.FormData]: (input: FormData) => + (Array.from(input.keys()) || []).reduce((formData, key) => { + const property = input.get(key); + formData.append( + key, + property instanceof Blob + ? property + : typeof property === "object" && property !== null + ? JSON.stringify(property) + : `${property}`, + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? {"Content-Type": type} : {}), + }, + signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, + body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), + }).then(async (response) => { + const r = response.clone() as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then((data) => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch((e) => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data; + }); + }; +} + +/** + * @title Träwelling API + * @version 1.0.0 - alpha + * @license Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0.html) + * @baseUrl https://traewelling.de/api/v1 + * @contact + * + * Träwelling user API description. This is an incomplete documentation with still many errors. The API is currently not yet stable. Endpoints are still being restructured. Both the URL and the request or body can be changed. Breaking changes will be announced on GitHub: https://github.com/Traewelling/traewelling/blob/develop/API_CHANGELOG.md + */ +export class Api extends HttpClient { + auth = { + /** + * No description + * + * @tags Auth + * @name LogoutUser + * @summary Logout & invalidate current bearer token + * @request POST:/auth/logout + * @secure + */ + logoutUser: (params: RequestParams = {}) => + this.request< + { + /** @example "success" */ + status?: any; + }, + void + >({ + path: `/auth/logout`, + method: "POST", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Get all profile information about the authenticated user + * + * @tags Auth, User + * @name GetAuthenticatedUser + * @summary Get authenticated user information + * @request GET:/auth/user + * @secure + */ + getAuthenticatedUser: (params: RequestParams = {}) => + this.request< + { + data?: UserAuthResource; + }, + void + >({ + path: `/auth/user`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description This request issues a new Bearer-Token with a new expiration date while also revoking the old * token. + * + * @tags Auth + * @name RefreshToken + * @summary Refresh Bearer Token + * @request POST:/auth/refresh + * @secure + */ + refreshToken: (params: RequestParams = {}) => + this.request< + { + data?: BearerTokenResponse; + }, + void + >({ + path: `/auth/refresh`, + method: "POST", + secure: true, + format: "json", + ...params, + }), + }; + event = { + /** + * @description Returns slug, name and duration for an event + * + * @tags Events + * @name GetEvent + * @summary [Auth optional] Get basic information for event + * @request GET:/event/{slug} + * @secure + */ + getEvent: (slug?: string, params: RequestParams = {}) => + this.request< + { + data?: EventResource; + }, + void + >({ + path: `/event/${slug}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns overall travelled distance and duration for an event + * + * @tags Events + * @name GetEventDetails + * @summary [Auth optional] Get additional information for event + * @request GET:/event/{slug}/details + * @secure + */ + getEventDetails: (slug?: string, params: RequestParams = {}) => + this.request< + { + data?: EventDetailsResource; + }, + void + >({ + path: `/event/${slug}/details`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns all for user visible statuses for an event + * + * @tags Events + * @name GetEventStatuses + * @summary [Auth optional] Get paginated statuses for event + * @request GET:/event/{slug}/statuses + * @secure + */ + getEventStatuses: ( + slug?: string, + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: StatusResource[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/event/${slug}/statuses`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Submit a possible event for our administrators to publish + * + * @tags Events + * @name SuggestEvent + * @summary Suggest a event + * @request POST:/event + * @secure + */ + suggestEvent: (data: EventSuggestion, params: RequestParams = {}) => + this.request({ + path: `/event`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + }; + events = { + /** + * @description Returns all active or upcoming events for the given timestamp. Default timestamp is now. If upcoming is set to true, all events ending after the timestamp are returned. + * + * @tags Events + * @name GetEvents + * @summary [Auth optional] Show active or upcoming events for the given timestamp + * @request GET:/events + * @secure + */ + getEvents: ( + query?: { + /** + * The timestamp of view. Default is now. + * @example "2022-08-01T12:00:00+02:00" + */ + timestamp?: string; + /** Show only upcoming events */ + upcoming?: boolean; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: EventResource[]; + }, + void + >({ + path: `/events`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + }; + activeEvents = { + /** + * @description DEPRECATED - USE /events - removed after 2024-08 + * + * @tags Events + * @name GetActiveEvents + * @summary DEPRECATED - USE /events - removed after 2024-08 + * @request GET:/activeEvents + */ + getActiveEvents: (params: RequestParams = {}) => + this.request({ + path: `/activeEvents`, + method: "GET", + ...params, + }), + }; + user = { + /** + * No description + * + * @tags User/Follow + * @name CreateFollow + * @summary Follow a user + * @request POST:/user/{id}/follow + * @secure + */ + createFollow: (id?: number, params: RequestParams = {}) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/follow`, + method: "POST", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags User/Follow + * @name DestroyFollow + * @summary Unfollow a user + * @request DELETE:/user/{id}/follow + * @secure + */ + destroyFollow: (id?: number, params: RequestParams = {}) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/follow`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags User/Follow + * @name RemoveFollower + * @summary Remove a follower + * @request DELETE:/user/removeFollower + * @secure + */ + removeFollower: ( + data: { + /** + * userId + * ID of the to-be-unfollowed user + * @format int + * @example 1 + */ + userId?: any; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/user/removeFollower`, + method: "DELETE", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + + /** + * No description + * + * @tags User/Follow + * @name AcceptFollowRequest + * @summary Accept a follow request + * @request PUT:/user/acceptFollowRequest + * @secure + */ + acceptFollowRequest: ( + data: { + /** + * userId + * ID of the user who sent the follow request + * @format int + * @example 1 + */ + userId?: any; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/user/acceptFollowRequest`, + method: "PUT", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + + /** + * No description + * + * @tags User/Follow + * @name RejectFollowRequest + * @summary Reject a follow request + * @request DELETE:/user/rejectFollowRequest + * @secure + */ + rejectFollowRequest: ( + data: { + /** + * userId + * ID of the user who sent the follow request + * @format int + * @example 1 + */ + userId?: any; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/user/rejectFollowRequest`, + method: "DELETE", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + + /** + * @description This request returns whether the currently logged-in user has an active check-in or not. + * + * @tags Auth + * @name UserState + * @summary User state + * @request GET:/user/statuses/active + * @secure + */ + userState: (params: RequestParams = {}) => + this.request< + { + data?: StatusResource; + }, + void + >({ + path: `/user/statuses/active`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns paginated statuses of a single user specified by the username + * + * @tags User, Status + * @name GetStatusesForUser + * @summary [Auth optional] Get paginated statuses for single user + * @request GET:/user/{username}/statuses + * @secure + */ + getStatusesForUser: ( + username?: any, + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: StatusResource[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/user/${username}/statuses`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns general information, metadata and statistics for a user + * + * @tags User + * @name ShowUser + * @summary [Auth optional] Get information for single user + * @request GET:/user/{username} + * @secure + */ + showUser: ( + username?: any, + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${username}`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Block a specific user. That user will not be able to see your statuses or profile information, * and cannot send you follow requests. Public statuses are still visible through the incognito mode. + * + * @tags User/Hide and Block + * @name CreateBlock + * @summary Block a user + * @request POST:/user/{id}/block + * @secure + */ + createBlock: ( + id: string, + data: { + /** + * userId + * ID of the to-be-blocked user + * @format int + * @example 1 + */ + userId?: any; + }, + params: RequestParams = {}, + ) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/block`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Unblock a specific user. They are now able to see your statuses and profile information again, * and send you follow requests. + * + * @tags User/Hide and Block + * @name DestroyBlock + * @summary Unmute a user + * @request DELETE:/user/{id}/block + * @secure + */ + destroyBlock: ( + id: string, + data: { + /** + * userId + * ID of the to-be-unblocked user + * @format int + * @example 1 + */ + userId?: any; + }, + params: RequestParams = {}, + ) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/block`, + method: "DELETE", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Mute a specific user. That way they will not be shown on your dashboard and in the active * journeys tab + * + * @tags User/Hide and Block + * @name CreateMute + * @summary Mute a user + * @request POST:/user/{id}/mute + * @secure + */ + createMute: (id?: number, params: RequestParams = {}) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/mute`, + method: "POST", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Unmute a specific user. That way they will be shown on your dashboard and in the active * journeys tab again + * + * @tags User/Hide and Block + * @name DestroyMute + * @summary Unmute a user + * @request DELETE:/user/{id}/mute + * @secure + */ + destroyMute: (id?: number, params: RequestParams = {}) => + this.request< + { + /** User model */ + data?: User; + }, + void + >({ + path: `/user/${id}/mute`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns paginated statuses of a single user specified by the username + * + * @tags User + * @name SearchUsers + * @summary Get paginated statuses for single user + * @request GET:/user/search/{query} + * @secure + */ + searchUsers: ( + query?: any, + queryParams?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: User[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/user/search/${query}`, + method: "GET", + query: queryParams, + secure: true, + format: "json", + ...params, + }), + }; + settings = { + /** + * No description + * + * @tags User/Follow, Settings + * @name GetFollowers + * @summary List all followers + * @request GET:/settings/followers + * @secure + */ + getFollowers: (params: RequestParams = {}) => + this.request< + { + data?: User[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/settings/followers`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags User/Follow, Settings + * @name GetFollowRequests + * @summary List all followers + * @request GET:/settings/follow-requests + * @secure + */ + getFollowRequests: (params: RequestParams = {}) => + this.request< + { + data?: User[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + any + >({ + path: `/settings/follow-requests`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags User/Follow, Settings + * @name GetFollowings + * @summary List all users the current user is following + * @request GET:/settings/followings + * @secure + */ + getFollowings: (params: RequestParams = {}) => + this.request< + { + data?: User[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + any + >({ + path: `/settings/followings`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Accept the current privacy policy + * + * @tags Settings + * @name AcceptPrivacyPolicy + * @summary Accept the current privacy policy + * @request POST:/settings/acceptPrivacy + * @secure + */ + acceptPrivacyPolicy: (params: RequestParams = {}) => + this.request({ + path: `/settings/acceptPrivacy`, + method: "POST", + secure: true, + ...params, + }), + + /** + * @description Get the current user's profile settings + * + * @tags Settings + * @name GetProfileSettings + * @summary Get the current user's profile settings + * @request GET:/settings/profile + * @secure + */ + getProfileSettings: (params: RequestParams = {}) => + this.request< + { + /** Model for all user profile settings */ + data?: UserProfileSettings; + }, + void + >({ + path: `/settings/profile`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Update the current user's profile settings + * + * @tags Settings + * @name UpdateProfileSettings + * @summary Update the current user's profile settings + * @request PUT:/settings/profile + * @secure + */ + updateProfileSettings: ( + data: { + /** + * @maxLength 25 + * @example "gertrud123" + */ + username?: string; + /** + * @maxLength 50 + * @example "Gertrud" + */ + displayName?: string; + /** @example false */ + privateProfile?: boolean | null; + /** @example false */ + preventIndex?: boolean | null; + /** @example 1 */ + privacyHideDays?: number | null; + defaultStatusVisibility?: number | null; + mastodonVisibility?: number | null; + mapProvider?: string | null; + }, + params: RequestParams = {}, + ) => + this.request< + { + /** Model for all user profile settings */ + data?: UserProfileSettings; + }, + void + >({ + path: `/settings/profile`, + method: "PUT", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Deletes the Account for the user and all posts created by it + * + * @tags Settings + * @name DeleteUserAccount + * @summary Delete User Account + * @request DELETE:/settings/account + * @secure + */ + deleteUserAccount: ( + data: { + /** + * confirmation + * Username of the to be deleted account (needs to match the currently logged in + * * user) + * @example "Gertrud123" + */ + confirmation?: any; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/settings/account`, + method: "DELETE", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + }; + status = { + /** + * @description Returns array of users that liked the status. Can return an empty dataset when the status * author or the requesting user has deactivated likes + * + * @tags Likes + * @name GetLikesForStatus + * @summary [Auth optional] Get likes for status + * @request GET:/status/{id}/likes + * @secure + */ + getLikesForStatus: (id?: number, params: RequestParams = {}) => + this.request< + { + data?: User[]; + }, + void + >({ + path: `/status/${id}/likes`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Add like to status + * + * @tags Likes + * @name AddLikeToStatus + * @summary Add like to status + * @request POST:/status/{id}/like + * @secure + */ + addLikeToStatus: (id?: number, params: RequestParams = {}) => + this.request< + { + data?: LikeResponse; + }, + void + >({ + path: `/status/${id}/like`, + method: "POST", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Removes like from status + * + * @tags Likes + * @name RemoveLikeFromStatus + * @summary Remove like from status + * @request DELETE:/status/{id}/like + * @secure + */ + removeLikeFromStatus: (id?: number, params: RequestParams = {}) => + this.request< + { + data?: LikeResponse; + }, + void + >({ + path: `/status/${id}/like`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns a single status Object, if user is authorized to see it + * + * @tags Status + * @name GetSingleStatus + * @summary [Auth optional] Get single statuses + * @request GET:/status/{id} + * @secure + */ + getSingleStatus: (id?: number, params: RequestParams = {}) => + this.request< + { + data?: StatusResource; + }, + void + >({ + path: `/status/${id}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Updates a single status Object, if user is authorized to + * + * @tags Status + * @name UpdateSingleStatus + * @summary Update a status + * @request PUT:/status/{id} + * @secure + */ + updateSingleStatus: (data: StatusUpdateBody, id?: number, params: RequestParams = {}) => + this.request< + { + data?: StatusResource; + }, + void + >({ + path: `/status/${id}`, + method: "PUT", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Deletes a single status Object, if user is authorized to + * + * @tags Status + * @name DestroySingleStatus + * @summary Destroy a status + * @request DELETE:/status/{id} + * @secure + */ + destroySingleStatus: (id?: number, params: RequestParams = {}) => + this.request({ + path: `/status/${id}`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns a collection of all visible tags for the given status, if user is authorized + * + * @tags Status + * @name GetTagsForStatus + * @summary Show all tags for a status which are visible for the current user + * @request GET:/status/{statusId}/tags + * @secure + */ + getTagsForStatus: (statusId?: number, params: RequestParams = {}) => + this.request< + { + data?: StatusTag[]; + }, + void + >({ + path: `/status/${statusId}/tags`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Creates a single StatusTag Object, if user is authorized to.

The key of a tag is free * text. You can choose it as you need it. However, please use a namespace for tags * (namespace:xxx) that only affect your own application.

For tags related to standard actions * we recommend the following tags in the trwl namespace:
*
    *
  • trwl:seat (i.e. 61)
  • *
  • trwl:wagon (i.e. 25)
  • *
  • trwl:ticket (i.e. BahnCard 100 first))
  • *
  • trwl:travel_class (i.e. 1, 2, business, economy, ...)
  • *
  • trwl:locomotive_class (BR424, BR450)
  • *
  • trwl:wagon_class (i.e. Bpmz)
  • *
  • trwl:role (i.e. Tf, Zf, Gf, Lokführer, conducteur de train, ...)
  • *
  • trwl:vehicle_number (i.e. 425 001, Tz9001, 123, ...)
  • *
  • trwl:passenger_rights (i.e. yes / no / ID of claim)
  • *
+ * + * @tags Status + * @name CreateSingleStatusTag + * @summary Create a StatusTag + * @request POST:/status/{statusId}/tags + * @secure + */ + createSingleStatusTag: (data: StatusTag, statusId?: number, params: RequestParams = {}) => + this.request< + { + /** StatusTag model */ + data?: StatusTag; + }, + void + >({ + path: `/status/${statusId}/tags`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Updates a single StatusTag Object, if user is authorized to + * + * @tags Status + * @name UpdateSingleStatusTag + * @summary Update a StatusTag + * @request PUT:/status/{statusId}/tags/{tagKey} + * @secure + */ + updateSingleStatusTag: (data: StatusTag, statusId?: number, tagKey?: string, params: RequestParams = {}) => + this.request< + { + /** StatusTag model */ + data?: StatusTag; + }, + void + >({ + path: `/status/${statusId}/tags/${tagKey}`, + method: "PUT", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Deletes a single StatusTag Object, if user is authorized to + * + * @tags Status + * @name DestroySingleStatusTag + * @summary Destroy a StatusTag + * @request DELETE:/status/{statusId}/tags/{tagKey} + * @secure + */ + destroySingleStatusTag: (statusId?: number, tagKey?: string, params: RequestParams = {}) => + this.request({ + path: `/status/${statusId}/tags/${tagKey}`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + }; + notifications = { + /** + * @description Returns count of unread notifications of a authenticated user + * + * @tags Notifications + * @name GetUnreadCount + * @summary Get count of unread notifications for authenticated user + * @request GET:/notifications/unread/count + * @secure + */ + getUnreadCount: (params: RequestParams = {}) => + this.request< + { + /** @example 2 */ + data?: number; + }, + void + >({ + path: `/notifications/unread/count`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns paginated notifications of a authenticated + * + * @tags Notifications + * @name ListNotifications + * @summary Get paginated notifications for authenticated user + * @request GET:/notifications + * @secure + */ + listNotifications: (params: RequestParams = {}) => + this.request< + { + data?: Notification[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/notifications`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Notifications + * @name MarkAsRead + * @summary Mark notification as read + * @request PUT:/notifications/read/{id} + * @secure + */ + markAsRead: (id?: string, params: RequestParams = {}) => + this.request< + { + /** Notification model */ + data?: Notification; + }, + void + >({ + path: `/notifications/read/${id}`, + method: "PUT", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Notifications + * @name MarkAsUnread + * @summary Mark notification as unread + * @request PUT:/notifications/unread/{id} + * @secure + */ + markAsUnread: (id?: string, params: RequestParams = {}) => + this.request< + { + /** Notification model */ + data?: Notification; + }, + void + >({ + path: `/notifications/unread/${id}`, + method: "PUT", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Notifications + * @name MarkAllAsRead + * @summary Mark all notification as read + * @request PUT:/notifications/read/all + * @secure + */ + markAllAsRead: (params: RequestParams = {}) => + this.request({ + path: `/notifications/read/all`, + method: "PUT", + secure: true, + format: "json", + ...params, + }), + }; + operators = { + /** + * No description + * + * @tags Checkin + * @name Bcfcf8686980Cf0Fcdc751B2E13Fa4F7 + * @summary Get a list of all operators. + * @request GET:/operators + */ + bcfcf8686980Cf0Fcdc751B2E13Fa4F7: (params: RequestParams = {}) => + this.request< + { + data?: OperatorResource[]; + }, + void + >({ + path: `/operators`, + method: "GET", + format: "json", + ...params, + }), + }; + static = { + /** + * @description Get the current privacy policy + * + * @tags Settings + * @name E649Bec35Ba50765Db023E745233Eda9 + * @summary Get the current privacy policy + * @request GET:/static/privacy + */ + e649Bec35Ba50765Db023E745233Eda9: (params: RequestParams = {}) => + this.request< + { + data?: { + /** @example "2022-01-05T16:26:14.000000Z" */ + validFrom?: any; + /** @example "This is the english privacy policy" */ + en?: any; + /** @example "Dies ist die deutsche Datenschutzerklärung" */ + de?: any; + }; + }, + any + >({ + path: `/static/privacy`, + method: "GET", + format: "json", + ...params, + }), + }; + report = { + /** + * No description + * + * @tags User, Status, Events + * @name Report + * @summary Report a Status, Event or User to the admins. + * @request POST:/report + * @secure + */ + report: ( + data: { + /** @example "Status" */ + subject_type: "Event" | "Status" | "User"; + /** @example 1 */ + subject_id: number; + /** @example "inappropriate" */ + reason: "inappropriate" | "implausible" | "spam" | "illegal" | "other"; + /** @example "The status is inappropriate because..." */ + description?: string | null; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/report`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + ...params, + }), + }; + leaderboard = { + /** + * No description + * + * @tags Leaderboard + * @name GetLeaderboard + * @summary [Auth optional] Get array of 20 best users + * @request GET:/leaderboard + * @secure + */ + getLeaderboard: (params: RequestParams = {}) => + this.request< + { + data?: LeaderboardUserResource[]; + }, + void + >({ + path: `/leaderboard`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Leaderboard + * @name GetLeaderboardByDistance + * @summary [Auth optional] Get leaderboard array sorted by distance + * @request GET:/leaderboard/distance + * @secure + */ + getLeaderboardByDistance: (params: RequestParams = {}) => + this.request< + { + data?: LeaderboardUserResource[]; + }, + void + >({ + path: `/leaderboard/distance`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Leaderboard + * @name GetLeaderboardByFriends + * @summary Get friends-leaderboard array sorted + * @request GET:/leaderboard/friends + * @secure + */ + getLeaderboardByFriends: (params: RequestParams = {}) => + this.request< + { + data?: LeaderboardUserResource[]; + }, + void + >({ + path: `/leaderboard/friends`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Leaderboard + * @name GetMonthlyLeaderboard + * @summary [Auth optional] Get leaderboard array for a specific month + * @request GET:/leaderboard/{month} + * @secure + */ + getMonthlyLeaderboard: (month?: string, params: RequestParams = {}) => + this.request< + { + data?: LeaderboardUserResource[]; + }, + void + >({ + path: `/leaderboard/${month}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + statistics = { + /** + * No description + * + * @tags Statistics + * @name GetStatistics + * @summary Get personal statistics + * @request GET:/statistics + * @secure + */ + getStatistics: ( + query?: { + /** + * Start date for the statistics + * @example "2021-01-01T00:00:00.000Z" + */ + from?: any; + /** + * End date for the statistics + * @example "2021-02-01T00:00:00.000Z" + */ + until?: any; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: { + /** The purpose of travel */ + purpose?: { + /** What type of travel (0=private, 1=business, 2=commute) did the user specify? */ + name?: Business; + /** @example 11 */ + count?: number; + /** + * Duration in + * * minutes + * @example 425 + */ + duration?: number; + }[]; + /** The categories of the travel */ + categories?: { + /** Category of transport. */ + name?: HafasTravelType; + /** @example 11 */ + count?: number; + /** + * Duration in minutes + * @example 425 + */ + duration?: number; + }[]; + /** The operators of the means of transport */ + operators?: { + /** @example "Gertruds Verkehrsgesellschaft mbH" */ + name?: any; + /** @example 10 */ + count?: number; + /** + * Duration in minutes + * @example 424 + */ + duration?: number; + }[]; + /** Shows the daily travel volume */ + time?: { + /** @example "2021-01-01T00:00:00.000Z" */ + date?: string; + /** @example 10 */ + count?: number; + /** + * Duration in minutes + * @example 424 + */ + duration?: number; + }[]; + }; + }, + void + >({ + path: `/statistics`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns all statuses and statistics for the requested day + * + * @tags Statistics + * @name GetDailyStatistics + * @summary Get statistics and statuses of one day + * @request GET:/statistics/daily/{date} + * @secure + */ + getDailyStatistics: ( + date: string, + query?: { + /** + * Timezone for the date. If not set, the user's timezone will be used. + * @example "Europe/Berlin" + */ + timezone?: string; + /** + * If this parameter is set, the polylines will be returned as well. Otherwise attribute is + * * null. + */ + withPolylines?: boolean; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: { + statuses?: StatusResource[]; + polylines?: FeatureCollection[]; + /** @example "74026" */ + totalDistance?: number; + /** @example "4711" */ + totalDuration?: number; + /** @example "42" */ + totalPoints?: number; + }; + }, + void + >({ + path: `/statistics/daily/${date}`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Statistics + * @name GetGlobalStatistics + * @summary Get global statistics of the last 4 weeks + * @request GET:/statistics/global + * @secure + */ + getGlobalStatistics: (params: RequestParams = {}) => + this.request< + { + data?: { + /** + * Globally travelled distance in meters + * @example 1000 + */ + distance?: number; + /** + * Globally travelled duration in minutes + * @example 1000 + */ + duration?: number; + /** + * Number of active users + * @example 1000 + */ + activeUsers?: number; + meta?: { + /** @example "2021-01-01T00:00:00.000000Z" */ + from?: any; + /** @example "2021-02-01T00:00:00.000000Z" */ + until?: any; + }; + }; + }, + any + >({ + path: `/statistics/global`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + dashboard = { + /** + * @description Returns paginated statuses of personal dashboard + * + * @tags Dashboard + * @name GetDashboard + * @summary Get paginated statuses of personal dashboard + * @request GET:/dashboard + * @secure + */ + getDashboard: ( + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: StatusResource[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/dashboard`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns paginated statuses of global dashboard + * + * @tags Dashboard + * @name GetGlobalDashboard + * @summary Get paginated statuses of global dashboard + * @request GET:/dashboard/global + * @secure + */ + getGlobalDashboard: ( + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: StatusResource[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/dashboard/global`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns paginated statuses of the authenticated user, that are more than 20 minutes in the * future + * + * @tags Dashboard + * @name GetFutureDashboard + * @summary Get paginated future statuses of current user + * @request GET:/dashboard/future + * @secure + */ + getFutureDashboard: ( + query?: { + /** Page of pagination */ + page?: number; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: StatusResource[]; + /** pagination links */ + links?: Links; + /** Pagination meta data */ + meta?: PaginationMeta; + }, + void + >({ + path: `/dashboard/future`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + }; + statuses = { + /** + * @description Returns all currently active statuses that are visible to the (un)authenticated user + * + * @tags Status + * @name GetActiveStatuses + * @summary [Auth optional] Get active statuses + * @request GET:/statuses + * @secure + */ + getActiveStatuses: (params: RequestParams = {}) => + this.request< + { + data?: StatusResource[]; + }, + void + >({ + path: `/statuses`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns a collection of all visible tags for the given statuses, if user is authorized + * + * @tags Status + * @name GetTagsForMultipleStatuses + * @summary Show all tags for multiple statuses which are visible for the current user + * @request GET:/statuses/{statusIds}/tags + * @secure + */ + getTagsForMultipleStatuses: (statusIds?: string, params: RequestParams = {}) => + this.request< + { + data?: { + "1337"?: StatusTag[]; + "4711"?: StatusTag[]; + }; + }, + void + >({ + path: `/statuses/${statusIds}/tags`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + positions = { + /** + * @description Returns an array of live position objects for active statuses + * + * @tags Status + * @name GetLivePositionsForActiveStatuses + * @summary [Auth optional] get live positions for active statuses + * @request GET:/positions + * @secure + */ + getLivePositionsForActiveStatuses: (params: RequestParams = {}) => + this.request< + { + data?: LivePointDto[]; + }, + void + >({ + path: `/positions`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns an array of live position objects for given status IDs + * + * @tags Status + * @name GetLivePositionsForStatuses + * @summary [Auth optional] get live positions for given statuses + * @request GET:/positions/{ids} + * @secure + */ + getLivePositionsForStatuses: (ids?: string, params: RequestParams = {}) => + this.request< + { + data?: LivePointDto[]; + }, + void + >({ + path: `/positions/${ids}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + polyline = { + /** + * @description Returns GeoJSON for all requested status IDs + * + * @tags Status + * @name GetPolylines + * @summary [Auth optional] Get GeoJSON for statuses + * @request GET:/polyline/{ids} + * @secure + */ + getPolylines: (ids?: string, params: RequestParams = {}) => + this.request< + { + data?: { + /** @example "FeatureCollection" */ + type?: any; + features?: Polyline[]; + }; + }, + void + >({ + path: `/polyline/${ids}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + stopovers = { + /** + * @description Returns all underway-stops for stations + * + * @tags Status + * @name GetStopOvers + * @summary [Auth optional] Get stopovers for statuses + * @request GET:/stopovers/{ids} + * @secure + */ + getStopOvers: (ids?: string, params: RequestParams = {}) => + this.request< + { + data?: { + /** Array of stopovers. Key describes trip id */ + "1"?: StopoverResource[]; + }; + }, + void + >({ + path: `/stopovers/${ids}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + station = { + /** + * @description Get departures from a station. + * + * @tags Checkin + * @name GetDepartures + * @summary Get departures from a station + * @request GET:/station/{id}/departures + * @secure + */ + getDepartures: ( + id: any, + query?: { + /** + * When to get the departures (default: now). If you omit the timezone, the datetime is interpreted as localtime. This is especially helpful when träwelling abroad. + * @format date-time + * @example "2020-01-01T12:00:00.000Z" + */ + when?: string; + /** Means of transport (default: all) */ + travelType?: TravelType; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: any[]; + meta?: { + /** train station model */ + station?: Station; + times?: { + /** + * @format date-time + * @example "2020-01-01T12:00:00.000Z" + */ + now?: string; + /** + * @format date-time + * @example "2020-01-01T11:45:00.000Z" + */ + prev?: string; + /** + * @format date-time + * @example "2020-01-01T12:15:00.000Z" + */ + next?: string; + }; + }; + }, + void + >({ + path: `/station/${id}/departures`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Checkin + * @name SetHomeStation + * @summary Set a station as home station + * @request PUT:/station/{id}/home + * @secure + */ + setHomeStation: (id: any, params: RequestParams = {}) => + this.request< + { + /** train station model */ + data?: Station; + }, + void + >({ + path: `/station/${id}/home`, + method: "PUT", + secure: true, + format: "json", + ...params, + }), + }; + trains = { + /** + * No description + * + * @tags Checkin + * @name GetTrainTrip + * @summary Get the stopovers and trip information for a given train + * @request GET:/trains/trip + * @secure + */ + getTrainTrip: ( + query: { + /** + * HAFAS trip ID (fetched from departures) + * @example "1|323306|1|80|17072022" + */ + hafasTripId: any; + /** + * line name for that train + * @example "S 4" + */ + lineName: any; + /** + * start point from where the stopovers should be desplayed + * @example 4711 + */ + start: any; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: { + /** @example 1 */ + id?: number; + /** Category of transport. */ + category?: HafasTravelType; + /** @example "4-a6s4-4" */ + number?: string; + /** @example "S 4" */ + lineName?: string; + /** @example "34427" */ + journeyNumber?: number; + /** train station model */ + origin?: Station; + /** train station model */ + destination?: Station; + stopovers?: StopoverResource[]; + }; + }, + void + >({ + path: `/trains/trip`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns the nearest station to the given coordinates + * + * @tags Checkin + * @name TrainStationsNearby + * @summary Location based search for stations + * @request GET:/trains/station/nearby + * @secure + */ + trainStationsNearby: ( + query: { + /** + * latitude + * @example 48.991 + */ + latitude: any; + /** + * longitude + * @example 8.4005 + */ + longitude: any; + }, + params: RequestParams = {}, + ) => + this.request< + { + data?: Station[]; + }, + void + >({ + path: `/trains/station/nearby`, + method: "GET", + query: query, + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Checkin + * @name CreateTrainCheckin + * @summary Create a checkin + * @request POST:/trains/checkin + * @secure + */ + createTrainCheckin: (data: CheckinRequestBody, params: RequestParams = {}) => + this.request({ + path: `/trains/checkin`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description This request returns an array of max. 10 station objects matching the query. **CAUTION:** All * slashes (as well as encoded to %2F) in {query} need to be replaced, preferrably by a space (%20) + * + * @tags Checkin + * @name TrainStationAutocomplete + * @summary Autocomplete for stations + * @request GET:/trains/station/autocomplete/{query} + * @secure + */ + trainStationAutocomplete: (query?: any, params: RequestParams = {}) => + this.request< + { + data?: StationResource[]; + }, + void + >({ + path: `/trains/station/autocomplete/${query}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description This request returns an array of max. 10 most recent station objects that the user has arrived * at. + * + * @tags Checkin + * @name TrainStationHistory + * @summary History for stations + * @request GET:/trains/station/history + * @secure + */ + trainStationHistory: (params: RequestParams = {}) => + this.request< + { + data?: Station[]; + }, + void + >({ + path: `/trains/station/history`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + }; + webhooks = { + /** + * @description Returns all webhooks which are created for the current user and which the current authorized applicaton has access to. + * + * @tags Webhooks + * @name GetWebhooks + * @summary Get webhooks for current user and current application + * @request GET:/webhooks + * @secure + */ + getWebhooks: (params: RequestParams = {}) => + this.request< + { + data?: Webhook[]; + }, + void + >({ + path: `/webhooks`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * @description Returns a single webhook Object, if user and application is authorized to see it + * + * @tags Webhooks + * @name GetSingleWebhook + * @summary Get single webhook + * @request GET:/webhooks/{id} + * @secure + */ + getSingleWebhook: (id?: number, params: RequestParams = {}) => + this.request< + { + /** Webhook model */ + data?: Webhook; + }, + void + >({ + path: `/webhooks/${id}`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Webhooks + * @name DeleteWebhook + * @summary Delete a webhook if the user and application are authorized to do + * @request DELETE:/webhooks/{id} + * @secure + */ + deleteWebhook: (id?: number, params: RequestParams = {}) => + this.request({ + path: `/webhooks/${id}`, + method: "DELETE", + secure: true, + format: "json", + ...params, + }), + }; +} diff --git a/resources/views/about.blade.php b/resources/views/about.blade.php deleted file mode 100644 index 37fb7b23d..000000000 --- a/resources/views/about.blade.php +++ /dev/null @@ -1,166 +0,0 @@ -@extends('layouts.app') - -@section('title', 'FAQ - ' . __('about.faq-heading')) -@section('meta-robots', 'index') -@section('meta-description', __('about.block1')) -@section('canonical', route('static.about')) - -@section('content') - - -
-
-
-
-
-

- - {{ __('about.heading') }} -

-

{{ __('about.block1') }}

-
-
-
-
-

- - {{ __('about.who-heading') }} -

-

- {{ __('about.who0') }} - {{ __('about.who1') }} - {!! __('about.who2', ['link' => 'https://github.com/Traewelling/traewelling/graphs/contributors']) !!} - {!! __('about.who3', ['link' => url('humans.txt')]) !!} -

-
-
-
-
-

- - {{ __('about.dev') }} -

-

- {{ __('about.dev.1') }} - {{ __('about.dev.2') }} - {{ __('about.dev.3') }} - {{ __('about.dev.4') }} -

-
-
-
-
-

- - {{ __('about.name-heading') }} -

-

{!! __('about.name') !!}

-
-
-
-
-

- - {{ __('about.events') }} -

-

{{__('about.events.description1')}}

-

{!! __('about.events.description2', ['link' => route('events')]) !!}

-

{!! __('about.events.description3') !!}

-

{{__('about.events.description4')}}

-
-
-
-
-
-
-

- - {{ __('about.missing-verification-email') }} -

-

- {{ __('about.missing-verification-email.description') }} - {{ __('about.missing-verification-email.description2', ['email' => config('mail.from.address')]) }} - {{ __('about.missing-verification-email.description3') }} -

-
-

- - {{ __('about.missing-verification-email.description4') }} - {{ __('about.missing-verification-email.description5') }} -

-
-
-
-
-

- - {{ __('about.no-train-heading') }} -

-

{{ __('about.no-train') }}

-
-
-
-
-

- - {{ __('about.points-heading') }} -

-

{{ __('about.points1') }}

- - - - - - - - - @foreach(\App\Enum\HafasTravelType::cases() as $category) - - - - - @endforeach - -
{{ __('about.productclass') }}{{ __('about.basepoints') }}
{{ __('transport_types.' . $category->value) }}{{config('trwl.base_points.train.' . $category->value, 1)}}
- -

{!! __('about.calculation') !!}

- -

- - {{__('about.points-real-time')}} -

-
-
-
-
-
-@endsection diff --git a/resources/views/admin/checkin/stationboard.blade.php b/resources/views/admin/checkin/stationboard.blade.php index cc8ed3980..e58b45043 100644 --- a/resources/views/admin/checkin/stationboard.blade.php +++ b/resources/views/admin/checkin/stationboard.blade.php @@ -126,7 +126,7 @@ class="table table-dark table-borderless table-hover table-striped m-0"> @@ -189,7 +189,7 @@ class="table table-dark table-borderless table-hover table-striped m-0"> diff --git a/resources/views/admin/checkin/trip.blade.php b/resources/views/admin/checkin/trip.blade.php index 93749400a..847d5e619 100644 --- a/resources/views/admin/checkin/trip.blade.php +++ b/resources/views/admin/checkin/trip.blade.php @@ -13,7 +13,8 @@ - + + @@ -22,6 +23,7 @@ + + @endsection diff --git a/resources/views/admin/reports/show.blade.php b/resources/views/admin/reports/show.blade.php index 84e94f30b..d8f2749e4 100644 --- a/resources/views/admin/reports/show.blade.php +++ b/resources/views/admin/reports/show.blade.php @@ -3,6 +3,7 @@ @section('title', 'Report ' . $report->id) @section('content') + @php /** @var \App\Models\Report $report */ @endphp
@@ -24,7 +25,30 @@ @endisset -
Subject
+ +
+
Subject
+
+ @if( $report->subject_type === \App\Models\Trip::class ) + + Trip #{{ $report->subject_id }} + + @elseif( $report->subject_type === \App\Models\User::class ) + + User #{{ $report->subject_id }} + + @elseif( $report->subject_type === \App\Models\Event::class ) + + Event #{{ $report->subject_id }} + + @elseif( $report->subject_type === \App\Models\Status::class ) + + Status #{{ $report->subject_id }} + + @endif +
+
+
Activity
{{ class_basename($report->subject_type) }} #{{ $report->subject_id }} @@ -54,7 +78,7 @@ @endif
Created at
-
{{ $report->created_at }}
+
{{ userTime($report->created_at, 'Y-m-d H:i:s', false) }}
@@ -66,6 +90,7 @@ Subject + ToDo: Show subject details depending on subject type
diff --git a/resources/views/admin/status/edit.blade.php b/resources/views/admin/status/edit.blade.php index 9cd68ac5f..46d7b622e 100644 --- a/resources/views/admin/status/edit.blade.php +++ b/resources/views/admin/status/edit.blade.php @@ -61,7 +61,7 @@ @endisset
- {{ $status->checkin->trip_id }} + {{ $status->checkin->id }} ({{ $status->checkin->trip->source }}) @@ -90,7 +90,7 @@ @foreach($status->checkin->trip->stopovers as $stopover) @foreach($status->checkin->trip->stopovers as $stopover)