Compare commits

...

599 Commits

Author SHA1 Message Date
Eric Allam dfb00e84e9 Fixing pnpm lock file 2023-08-08 07:35:25 +01:00
github-actions[bot] b5a64545bb chore: Update version for release (#273)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-08 07:32:24 +01:00
Eric Allam 3cf3eaff48 @trigger.dev/supabase: You can now trigger on multiple database events in the same job 2023-08-07 17:27:49 +01:00
Matt Aitken 4ca758a3de job-catalog: added initial setup and CLI build step
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-08-07 15:07:58 +01:00
Matt Aitken da81537009 Update job-catalog instructions
`pnpm run trigger:dev` should be `pnpm run dev:trigger`
2023-08-07 14:57:08 +01:00
Eric Allam 7c1b13ba3b Increase webhook delivery attempts to the max 25 attempts over 3 days 2023-08-07 14:25:23 +01:00
D-K-P 4b654e5b3b Fixed broken docs link 2023-08-07 13:29:11 +01:00
Eric Allam 726a50ace7 Point to the workspace version of @trigger.dev/sdk 2023-08-07 13:17:36 +01:00
github-actions[bot] 9deffb67c4 chore: Update version for release (#267)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-07 13:08:27 +01:00
Matt Aitken ff04bf44ee CLI: detect package manager using artifacts if they exist (#261)
* Detect package manager from artifacts (e.g. package-lock.json) and fallback to process.env.npm_config_user_agent

* Added a changeset
2023-08-07 13:01:22 +01:00
Matt Aitken e740297829 Cli: improve Next.js project detection (#262)
* Detect presence of a “next” dependency

* Use a strict undefined check instead

* Changeset: Detect Next.js project by looking at dependencies, not next.config.js

* Read a package json file

* First check for next.config file, otherwise use next dependency

* Update changeset description
2023-08-07 13:01:03 +01:00
D-K-P a31705e198 Added text summarizer / github issue reminder jobs to the examples table 2023-08-07 12:58:43 +01:00
Eric Allam f0bdf53364 Auto index production endpoints every 10 minutes 2023-08-07 12:49:37 +01:00
Matt Aitken 9638499163 Add your first endpoint in the dashboard (#269)
* Created the sheet

* Started work on the resource route

* Created ValidateCreateEndpointService and the EndpointValidateApi (name TBC)

* The TriggerClient responds with the id

* Tidied some stuff up

* Alternative: EndpointApi doesn’t take endpointSlug. Instead just Ping() does

* Create cool-snakes-deny.md

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
2023-08-07 11:28:34 +01:00
Matt Aitken 9104c252b7 Telemetry (new user) (#268)
* Removed old telemetry

* Started adding TriggerClient telemetry

* Added a log if the telemetry client is created, event renamed to “user.created”
2023-08-07 11:28:13 +01:00
Matt Aitken 72cf345602 Added “thumbsRating” to docs pages 2023-08-07 10:50:02 +01:00
jemiluv8 d5b8f8299d update cli init to create index file in examples folder (#225)
* update cli init to create index file in examples folder

* add patch changeset

* Create silly-baboons-join.md

* remove comment in router file
add comment in jobs index to instruct them to

* rename examplesIndex to examplesIndexFileName

* import jobs module in routeContent for createTriggerPageRoute just as we did for createTriggerAppRoute

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2023-08-06 16:42:32 +01:00
Matt Aitken dd22b88d25 Latest lockfile… 🙄 2023-08-03 22:52:46 +01:00
github-actions[bot] 62b9c5879c chore: Update version for release (#248) 2023-08-03 22:47:23 +01:00
Eric Allam 1b0973fbc1 Fixed error when configuring a new endpoint failed and fixed ping to now throw an error when parsing JSON
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-08-03 16:02:16 +01:00
Eric Allam fc78854ed4 @trigger.dev/cli: Don't require the TRIGGER_API_URL to be set (it has a default) 2023-08-03 14:49:46 +01:00
Eric Allam ed15bb0618 Another small supabase tweak 2023-08-03 13:09:56 +01:00
Eric Allam ced9034428 Another small supabase doc fix 2023-08-03 13:08:42 +01:00
Eric Allam 7ec329d5d6 Fixed broken image in supabase docs 2023-08-03 13:06:23 +01:00
Eric Allam 81180999de The dev command should use a POST request when doing the PING to the local server 2023-08-03 13:01:08 +01:00
Eric Allam feaa79ecf3 @trigger.dev/supabase: Automatically enable database webhooks when using triggers 2023-08-03 13:00:29 +01:00
Eric Allam d34dc0847c More OpenAI examples 2023-08-03 09:55:07 +01:00
Eric Allam 1b7c7520b4 new Job -> client.defineJob 2023-08-03 09:53:15 +01:00
Eric Allam 34d77e830c Incorporate improved OpenAI docs from old repo 2023-08-03 09:51:25 +01:00
Eric Allam 87c302bf26 Document how to enable supabase database webhooks 2023-08-03 09:39:58 +01:00
Eric Allam d462c901f5 A couple of quickstart tweaks 2023-08-02 17:16:39 +01:00
D-K-P 55c3d79cc8 Deleted the info box on the integration pages 2023-08-02 17:11:41 +01:00
Eric Allam ce901c92e4 Update @trigger.dev/supabase to the latest supabase-management-js 2023-08-02 17:01:02 +01:00
Matt Aitken 0fd409c6dc Latest lockfile 2023-08-02 16:08:50 +01:00
github-actions[bot] f4e7379768 chore: Update version for release (#247)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-02 16:06:27 +01:00
Matt Aitken ee99191fb4 Sync all package version numbers 2023-08-02 15:58:56 +01:00
Matt Aitken 18124ae32a Sync all of the package version numbers 2023-08-02 15:45:20 +01:00
Eric Allam de3373b8f9 Fixed pnpm lock file
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-08-02 15:40:26 +01:00
Eric Allam bdb4e6e2bd Only publish latest docker images when tagged with v.docker.x.y.z 2023-08-02 15:38:53 +01:00
Matt Aitken 0a790de2c1 core version changed to 1.0.0. Dependencies for core set to ^1.0.0 2023-08-02 15:21:19 +01:00
Eric Allam ffb55bfde6 Add react package docs 2023-08-02 15:18:03 +01:00
github-actions[bot] c0fbe56f79 chore: Update version for release (#246)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-02 15:10:36 +01:00
Eric Allam 342ab0d471 Adding docs back into this repo 2023-08-02 15:09:35 +01:00
Matt Aitken aa9fe7d408 core made public, react and sdk now have it as a dependency (not dev) 2023-08-02 15:07:14 +01:00
Eric Allam f499c10f09 Run pnpm i to fix pnpm-lock
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-08-02 14:19:46 +01:00
github-actions[bot] 45ed7cdf38 chore: Update version for release (#238)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-02 14:18:12 +01:00
Eric Allam fa271aaed2 Tweaked the SDK package readme 2023-08-02 13:42:45 +01:00
Eric Allam 8883476dcb Specify limited paths that will cause our github workflows to run 2023-08-02 13:27:44 +01:00
Eric Allam c1755ff85b Removed error logging from the stripe integration 2023-08-02 11:53:42 +01:00
Eric Allam 6d04f6c62a Added default retry settings for all integration tasks 2023-08-02 11:53:09 +01:00
Eric Allam 53363711bb Fixed type errors in the cli 2023-08-02 11:50:50 +01:00
Eric Allam 442ee45e97 Upgrade to Remix 1.19.2-pre.0 to try and fix premature close issue
https://github.com/remix-run/remix/issues/4993#issuecomment-1661221726
2023-08-02 11:49:04 +01:00
Eric Allam 8405b337f9 Fix seed files by copying any .node query engine file in entrypoint.sh 2023-08-02 10:21:41 +01:00
Matt Aitken facae926ce Added telemetry to the CLI (#242)
* Basic CLI telemetry

* Telemetry for init command

* Better error handling

* Fix for “Module not found: Can't resolve 'encoding' in” node-fetch warning

* Telemetry for dev and tidied up init

* Minor improvements to the telemetry

* Latest package lock file

* Set the PostHog key to the prod one

* Changesets

* Formatted
2023-08-02 09:47:08 +01:00
Eric Allam 9544624e00 Adding compound triggers to Stripe and added a WIP express adapter 2023-08-01 16:06:42 +01:00
Matt Aitken a489fd465f @trigger.dev/init and @trigger.dev/internal references updated to the new package names 2023-08-01 12:10:49 +01:00
Matt Aitken 9ff4c0dbd5 Project-wide Prettier setup (#237)
* Setup project-wide prettier

* Remove old workspace file

* Remove old debugging directives

* New top-level .prettierignore

* Updated Prettier config settings

* Contrubuting guide: Fix for some bad code blocks

* Added more ignores

* Improved the format script command

* printWidth set to 100

* Formatted entire repo (pnpm run format)
2023-08-01 10:21:22 +01:00
Eric Allam 36ec16babe feat: encrypt values in SecretStore using an encryption key (#240)
* feat: encrypt values in SecretStore using an encryption key and `aes-256-gcm`

* Stopped using apiCors in the connectionId API endpoint
2023-08-01 10:11:47 +01:00
Matt Aitken 92233f2e90 Core package and move to @latest (#235)
* Changeset should ignore the example projects

* Removed note in Contributing instructions about not adding a changeset for internal

* Renamed @trigger.dev/internal to @trigger.dev/core. Set sdk and internal to be ES2020, so we don’t get errors about private identifiers

* Env vars in nextjs-example use square bracket syntax to avoid Turbo Repo errors

* Set the tsconfigs back for core and sdk

* @examples/nextjs compile error with undefined tasks

* Set the example projects to use ES2015 to avoid private modifier complaints

* package-tester example, which will use built packages

* package-tester package.json

* Created a readme for the package-tester

* Added all the packages to package-tester

* Create .env.local.example and readme instructions

* Added name to the package.json

* Upgraded @types/react and @types/react-dom everywhrre, so we can use server actions

* Upgraded @types/react and @types/react-dom everywhrre, so we can use server actions

* Reworked the react package build, so it generates separate files

* The SDK no longer bundles core

* package-tester setup with a server action and react hooks

* Added new SDK methods with logging

* Working tsup settings for client and server

* Explicit react hooks return types

* Added all the hooks for testing

* Added OpenAI step to the job to check types are still ok in integrations

* Get rid of rogue Changeset ignores

* Changeset: @trigger.dev/core is now a separate package

* Exited prerelease mode, added a changeset

* Changed @next to @latest

* Deleted seed.js, this shouldn’t be committed

* Ignore seed.js

* The webapp was importing @trigger.dev/core with a folder path rather package name…

* More webapp imports instead of @trigger.dev/core were a folder path

* Accidentally edited Stripe internal API url in the comment

* Added “sideEffects”: false so @trigger.dev/core is tree shaken by Remix
2023-07-31 18:47:34 +01:00
Ashutosh Bhadauriya efd590976c Feat: Detect if a Next.js project is running on the specified port before opening a tunnel (#214)
* Feat: Detect nextjs project

* Add check before opening tunnel

* Update dev.ts

* Add changeset
2023-07-31 16:43:44 +01:00
Matt Aitken 463cbed7df Merge pull request #226 from Chigala/feature/multiple-eventname-support
Feature/multiple eventname support in eventDispatcher
2023-07-31 16:14:25 +01:00
Matt Aitken 9138976dad Changeset: Multiple eventname support in eventDispatcher 2023-07-31 16:13:35 +01:00
Matt Aitken ace4d5f6ce Merge pull request #228 from jemiluv8/migration-contribution-guide
chore: add instructions for adding migrations to the project
2023-07-31 16:07:05 +01:00
Matt Aitken b09ecb0b06 Update CONTRIBUTING.md 2023-07-31 16:06:39 +01:00
Matt Aitken 10fef0f667 Improved the migration instructions 2023-07-31 16:06:12 +01:00
Eric Allam 3e5945fb9e Stop storing github access tokens for github authenticated users 2023-07-31 15:41:44 +01:00
Akhil fdf1b2148c feat: added git database tasks for github integration (#204)
* feat: added git database tasks for github integration

* feat: added examples for git database tasks
2023-07-31 09:50:41 +01:00
Eric Allam 7e4d72f959 Made the migration non-destructive and fixed other typechecking issues 2023-07-28 18:19:41 +01:00
Chigala 3e203ea967 Merge branch 'main' into feature/multiple-eventname-support 2023-07-28 17:53:31 +01:00
github-actions[bot] af56598a58 chore: Update version for release (next) (#234)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-28 16:27:36 +01:00
Eric Allam 1fef1fa5f8 Adding native property to get access to underlying client in local auth only integrations 2023-07-28 16:12:41 +01:00
github-actions[bot] bd2f68a51d chore: Update version for release (next) (#233)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-28 14:48:57 +01:00
Eric Allam 64477f6b3a @trigger.dev/sdk: Adding some type helpers for getting the payload and IO types from jobs and triggers 2023-07-28 14:42:28 +01:00
Eric Allam 8b09dfd697 Fix pnpm lock file 2023-07-27 22:14:40 +01:00
github-actions[bot] f330673143 chore: Update version for release (next) (#232)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-27 22:14:06 +01:00
Eric Allam 9351c05126 Initial @trigger.dev/stripe integration, with support for triggers and tasks (#231)
* Initial Stripe integration, with support for triggers and tasks

* Adding stripe integration to the catalog
2023-07-27 22:09:07 +01:00
Matt Aitken af722e54d8 Made the integration items capable of fitting more text 2023-07-27 19:59:20 +01:00
Matt Aitken 1eb89844f4 Added a description to the Supabase Management integration so the differences are obvious 2023-07-27 19:57:03 +01:00
Matt Aitken 004423aea3 Merge pull request #229 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-27 17:14:03 +01:00
github-actions[bot] 369c6245ab chore: Update version for release (next) 2023-07-27 16:07:50 +00:00
Matt Aitken b8ae937966 TSUP working properly 2023-07-27 17:05:43 +01:00
Matt Aitken 3b510a4f3d Put paths back 2023-07-27 16:30:56 +01:00
Matt Aitken b3e609d53d Experimenting with bundling 2023-07-27 16:10:19 +01:00
Matt Aitken 2f07a792e9 Removed some unused vars 2023-07-27 16:10:05 +01:00
Matt Aitken 078bf2bc9c React package bundling changed 2023-07-27 15:14:01 +01:00
Matt Aitken a7c8f0b90e Merge pull request #227 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-27 11:18:56 +01:00
Jemilu Mohammed 495acf4319 modify step 3 of migrations instructions 2023-07-27 10:17:40 +00:00
Jemilu Mohammed dce86119d4 add instructions for adding migrations to the project 2023-07-27 10:14:49 +00:00
github-actions[bot] 2c70b7a360 chore: Update version for release (next) 2023-07-27 10:00:21 +00:00
Matt Aitken c518185947 Merge pull request #221 from triggerdotdev/react-hooks
Get the status of runs and events, using the SDK or @trigger.dev/react hooks
2023-07-27 10:58:37 +01:00
Matt Aitken b314178de2 SDK Changeset: Added getEvent(), getRun() and getRuns() methods to the client 2023-07-27 10:22:52 +01:00
Chigala e0a2cec978 Chore: Modified eventTrigger to accept a string and an array of strings. 2023-07-27 00:09:03 +01:00
Matt Aitken d3608be1a0 Improved the environments help text – it now references server API keys 2023-07-26 22:00:16 +01:00
Matt Aitken 4c79129242 authenticateApiRequest() now returns an object with the apiKey, type and environment 2023-07-26 21:54:20 +01:00
Chigala 8ac83f1d59 added multiple event name support to the eventDispatcher 2023-07-26 21:18:03 +01:00
Matt Aitken 17e6d42989 Removed unused const in RunPresenter 2023-07-26 20:49:09 +01:00
Matt Aitken 0a3bbc1caf Merge remote-tracking branch 'origin/main' into react-hooks
# Conflicts:
#	apps/webapp/app/presenters/RunPresenter.server.ts
#	examples/nextjs-example/src/app/api/trigger/route.ts
#	examples/nextjs-example/tsconfig.json
2023-07-26 20:47:07 +01:00
Matt Aitken 89e1331e49 Better error handling for the job runs endpoint 2023-07-26 18:57:11 +01:00
Matt Aitken d934a53e6d Better error handling for the runs endpoint 2023-07-26 18:57:02 +01:00
Matt Aitken 2398d60b1a Added noExternal for the @trigger.dev/internal package 2023-07-26 18:54:12 +01:00
Matt Aitken 1bd755b628 Example project: Use POST for the test event 2023-07-26 18:52:07 +01:00
Eric Allam 861d5fa1b9 Fixed pnpm lock why does this happen 2023-07-26 18:22:20 +01:00
github-actions[bot] 0239fc6773 chore: Update version for release (next) (#222)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-26 18:20:42 +01:00
Eric Allam de57592d79 @trigger.dev/resend now retries failed email sends 2023-07-26 18:17:27 +01:00
Eric Allam f70f3a0a5d Fixed doubling of the params option 2023-07-26 18:14:07 +01:00
Matt Aitken 6c96b331dd Event API endpoint safe parsing and better error handling/responses 2023-07-26 18:03:32 +01:00
Eric Allam 767e09ee18 New integration: @trigger.dev/supabase (#203)
* WIP supabase integration

* supabase oauth working

* Supabase database triggers

* Specify postgres:14

* Limit refreshOAuthToken jobs to 10 attempts

* Better displaying types and removing onChange for now

* WIP on the supabase db client

* Finishing the supabase-js integration

* Adding changeset

* Added supabase to the integration catalogs, and added an optional icon to Integrations

* Reworking how we handle types for the triggers (wip)

* Update fully over to the new way to define supabase triggers

* Go back to using the type for the event name

* Add back in the icon to the JobListPresenter since it was moved from the ProjectPresenter

* Remove unused import
2023-07-26 18:01:27 +01:00
Matt Aitken b2f3ade7c7 Renamed Secret API keys to Server API keys 2023-07-26 17:55:34 +01:00
alubọsa 8f050b40c1 chore(docs): add changeset guide to contributing.md (#219)
* chore(docs): add changeset guide to contributing.md

* chore: text changes to changeset guide

* Update CONTRIBUTING.md

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
2023-07-26 17:30:58 +01:00
Ashutosh Bhadauriya 3cf01ff14c Add testing CLI changes guide to contributing.md (#220)
* Add testing CLI changes guide to contributing.md

* Update backticks
2023-07-26 17:18:42 +01:00
Matt Aitken 73e0084125 New lock file after main merge 2023-07-26 16:44:37 +01:00
Matt Aitken 093c9ec015 Merge remote-tracking branch 'origin/main' into react-hooks
# Conflicts:
#	pnpm-lock.yaml
2023-07-26 16:44:09 +01:00
Matt Aitken 475dddfbe4 Added private API keys to the environment page, with some explanatory text 2023-07-26 16:41:17 +01:00
Matt Aitken 188e4f2740 Added an outline styled for badges 2023-07-26 16:40:51 +01:00
Matt Aitken 4dc2b3184b Fix for missing pkApiKey in seedCloud script 2023-07-26 16:23:40 +01:00
Matt Aitken 139d541d86 Return the pkApiKey and sort the environments correctly (dev, preview, staging, prod) 2023-07-26 15:32:35 +01:00
Eric Allam 10798c0a05 Fixed the pnpm lock file 2023-07-26 14:45:42 +01:00
Matt Aitken 9c6ad11479 Latest lockfile 2023-07-26 14:43:54 +01:00
Matt Aitken 5527ef17f4 Merge remote-tracking branch 'origin/main' into react-hooks 2023-07-26 14:38:55 +01:00
Matt Aitken 981f4010a9 Shortened the delays in the hooks example to make it less annoying when testing 2023-07-26 14:38:05 +01:00
github-actions[bot] db7e8923c0 chore: Update version for release (next) (#212)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-26 14:37:48 +01:00
Hans Bignon K. Tognon 0eef5c2f01 Adds steps to contributing guide for running some jobs locally (#209)
* chore(contrib): added a few jobs

* chore(contrib): adds sample jobs for repo setup

* chore(contrib): refactor examples/jobs-starter

* docs(contrib): add steps to add starter jobs

* fix(contrib): typo

* chore(contrib): minor edits

* chore(contrib): update guide and starter code
2023-07-26 14:27:27 +01:00
Matt Aitken 08901f2e8a Merge remote-tracking branch 'origin/main' into react-hooks 2023-07-26 14:07:09 +01:00
Matt Aitken 51b365226d In the Job list, show the latest run across all environments
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-26 13:37:48 +01:00
Matt Aitken 0b89ee7eb5 ProjectsMenu truncates long titles and the badge is right-aligned 2023-07-26 13:37:18 +01:00
Sai Hari d7c4b24203 chore: 🤖 replace tsconfig package with tsconfck package (#218) 2023-07-26 07:22:16 +01:00
Matt Aitken d7352867ec Merge pull request #215 from katrieltsepelevish/fix/contributing-readme-running
Fix: Updated contributing README for running in development
2023-07-25 20:31:43 +01:00
Matt Aitken a2b97bd918 First changeset for the react package 2023-07-25 20:08:54 +01:00
Matt Aitken 5991a767f7 TriggerClient method for getRuns with a jobSlug. This will only work with the private API key 2023-07-25 20:00:19 +01:00
Matt Aitken 643eba263e Default to returning 20 tasks and set max to 50 2023-07-25 19:59:42 +01:00
Matt Aitken 4db8a3992b We don’t need the environmentId 2023-07-25 19:26:34 +01:00
Matt Aitken 03f7156749 API endpoint for getting the latest runs 2023-07-25 19:15:15 +01:00
Matt Aitken d9912e5548 jsdocs for the getEvent output 2023-07-25 18:13:14 +01:00
Matt Aitken 3c85c985d9 Added getEvent and getRun methods to TriggerClient. Added JSDocs to the types 2023-07-25 18:11:28 +01:00
Matt Aitken 0cf848b9b9 Show the job output in the example 2023-07-25 17:52:49 +01:00
Matt Aitken ef4056abde Only allow showing the task details if it’s a private API key 2023-07-25 17:52:40 +01:00
Matt Aitken 3a6c25d42d API authentication updated to work correctly with public and private keys 2023-07-25 17:52:20 +01:00
Matt Aitken 642c95027c Return an output from the hook test job 2023-07-25 17:51:25 +01:00
Matt Aitken 8a66a5c6c7 Migrate: Make the pkApiKey unique and non-null 2023-07-25 17:51:14 +01:00
Katriel Tsepelevish 7dcc55a083 Updated README 2023-07-25 19:33:16 +03:00
Matt Aitken 6989fec9d0 New API keys (public or private) now have 20 random characters 2023-07-25 17:06:01 +01:00
Matt Aitken bba190e4e9 Use apiCors function for the event endpoint too 2023-07-25 16:36:19 +01:00
Matt Aitken 6f670c8cc6 Use v5 react-query so we don’t need a QueryClientProvider. Improved the retrying behaviour 2023-07-25 16:36:09 +01:00
Katriel Tsepelevish d8626c624f Updated contributing README 2023-07-25 18:35:18 +03:00
D-K-P 454ad25b1d Updated the ID question in the CLI 2023-07-25 16:30:28 +01:00
D-K-P 4dce703b20 Changed the create a job in code callout from Quick start to Create a Job 2023-07-25 16:17:57 +01:00
D-K-P ce1e911c90 Fixed the github link on the examples job table 2023-07-25 16:13:08 +01:00
D-K-P f84f4dcee6 Updated the blank jobs page instructions 2023-07-25 16:02:03 +01:00
D-K-P bfa3757ee0 Updated the help & feedback copy 2023-07-25 15:16:33 +01:00
Matt Aitken 1fea5d7af3 React hooks are working in the example 2023-07-25 14:38:39 +01:00
Matt Aitken dd33bbf6a8 Reworked the next-js project to just have the app directory 2023-07-25 13:38:46 +01:00
Matt Aitken 74640b1a82 Custom react query context for the package 2023-07-25 13:38:32 +01:00
Eric Allam 51e6a74eda Lock turborepo to a specific version (1.10.10 breaks our Docker builds) 2023-07-25 13:37:40 +01:00
alubọsa 27a9812194 chore: use endpointId if it exists in package.json (#200)
* chore: use endpointId if package.json has it

* minor

* add changeset

* chore: include changeset

* chore: add proper summary to changeset

* chore: remove redundant changeset file
2023-07-25 13:27:35 +01:00
Eric Allam ba03a9ed20 Merge pull request #207 from jemiluv8/js-support-for-init-command
feat: Add Javascript support for init command
2023-07-25 13:09:56 +01:00
Matt Aitken f5c1c62a09 export SentEvent from the SDK 2023-07-24 17:48:07 +01:00
Matt Aitken 420e719db2 useEventRunDetails combines the two hooks 2023-07-24 17:23:32 +01:00
Matt Aitken c448f53964 Run hook fixes 2023-07-24 17:12:16 +01:00
Matt Aitken 72230f67d8 Improved the hook 2023-07-24 17:03:11 +01:00
Matt Aitken 4bdc28d7fc apiCors has 5 min maxAge 2023-07-24 17:03:02 +01:00
Matt Aitken eb5195768b Moved the run query to runs/$runId, instead of /tasks 2023-07-24 13:28:33 +01:00
Matt Aitken 5b6539d799 Improved the display of the react hooks in the example 2023-07-24 13:13:19 +01:00
Matt Aitken b1b471cf63 Removed the optional stuff from react zodfetch function 2023-07-24 13:13:09 +01:00
Matt Aitken bcd80463f3 Removed the parentId from the RunTaskSchema 2023-07-24 13:12:34 +01:00
Matt Aitken e2781cb090 Don’t return the task details unless they’re asked for, fix so the child tasks aren’t returned if they’re not asked for 2023-07-24 13:12:19 +01:00
D-K-P 09f906f7a1 Updated the invitation code page copy 2023-07-24 10:18:11 +01:00
Jemilu Mohammed 706ab20ced add "patch" change set for feature to support nextjs projects using javascript 2023-07-23 19:04:18 +00:00
Jemilu Mohammed 1dde615686 add change set for feature to support nextjs projects using javascript 2023-07-23 18:49:14 +00:00
Matt Aitken dda8be1fd0 Remove log 2023-07-23 19:26:25 +01:00
Matt Aitken 2888877463 Missed a cors response wrapper 2023-07-23 19:26:13 +01:00
Matt Aitken ac4253a187 Only refetch the run if it’s not complete 2023-07-23 19:26:00 +01:00
Matt Aitken a6f79d77d1 Run hook 2023-07-23 19:18:19 +01:00
Matt Aitken 4ca841a1d5 Query to get environment from either the apiKey or the pkApiKey 2023-07-23 19:06:34 +01:00
Matt Aitken 4d2a8728be Getting events is allowed with allowPublicKey 2023-07-23 19:06:18 +01:00
Matt Aitken ae3afdfc7e Allow public key api requests, if the flag is passed 2023-07-23 19:05:30 +01:00
Matt Aitken e135b06e2c Show errors, not working properly yet 2023-07-23 18:42:36 +01:00
Matt Aitken 106843be09 Checks for the correct public key format 2023-07-23 18:42:20 +01:00
Matt Aitken 022021dc4b Allow CORS for the GET events endpoint 2023-07-23 18:27:23 +01:00
Matt Aitken 8815b1608e Set the api url to localhost:3030 2023-07-23 18:26:59 +01:00
Matt Aitken 3dcd7743be Requests are happening but failing CORS 2023-07-23 18:03:39 +01:00
Matt Aitken 66fd838085 Setup the Nextjs example homepage with the TriggerProvider 2023-07-23 17:51:24 +01:00
Matt Aitken b04852c98c useQueryEvent 2023-07-23 17:51:03 +01:00
Matt Aitken ad19bfa283 Added zod fetch function 2023-07-23 17:50:51 +01:00
Matt Aitken ee86910caa Pass in default api url 2023-07-23 17:50:36 +01:00
Matt Aitken 9a4daaf024 Better errors for api key 2023-07-23 17:40:04 +01:00
Matt Aitken fcf1202af3 Added React package to Next.js example project 2023-07-23 17:36:55 +01:00
Matt Aitken 43c402ccae Initial React package with provider 2023-07-23 17:36:20 +01:00
Matt Aitken 24334a1e78 API client methods for getting event and run data 2023-07-23 16:25:13 +01:00
Matt Aitken 37e27510de Return some run info as well 2023-07-23 16:24:11 +01:00
Matt Aitken fe35e8ea39 Return the updatedAt time as well 2023-07-23 16:03:15 +01:00
Matt Aitken db49879584 API endpoint for getting event details 2023-07-23 16:02:02 +01:00
Matt Aitken df68305f28 Don’t get subtasks by default when using GET on API tasks 2023-07-23 15:41:13 +01:00
Matt Aitken 078b10ccea API endpoint for getting tasks changed to return more selective info. Use taskListToTree 2023-07-23 15:34:05 +01:00
Matt Aitken 1f8a36efb4 Changed task query from getting children to creating tree afterwards from all the tasks 2023-07-23 14:27:31 +01:00
Jemilu Mohammed b0c386ff87 fix bug when detecting app dir for js project
fix typo in createTriggerAppRoute
2023-07-22 20:38:10 +00:00
Jemilu Mohammed 8b69e06f79 rely on jsconfig.json file if javascript project revert unnecessary workarounds 2023-07-22 17:39:19 +00:00
Jemilu Mohammed 24aa4c7406 fix error in jobsPathPrefix and routePathPrefix for Javascript projects 2023-07-22 16:38:24 +00:00
Jemilu Mohammed 621c45aaf1 remove redundant Job import from @trigger.dev/sdk in jobsContent template string 2023-07-22 16:09:16 +00:00
Jemilu Mohammed 9f67b83bc5 fix formatting 2023-07-22 16:03:03 +00:00
Jemilu Mohammed da7dfd4bca revert files added wrongly 2023-07-22 16:01:44 +00:00
Jemilu Mohammed 07250f4ace Add support for Javascript nextjs projects in
removed assertion for whether the next project is using typescript
modify getPathAlias to work with non ts projects
2023-07-22 15:58:42 +00:00
Jemilu Mohammed 8b5055d9d3 initial commit for js example 2023-07-22 14:50:22 +00:00
Eric Allam ec36918061 Merge pull request #188 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-21 09:34:04 +01:00
github-actions[bot] c895722ffc chore: Update version for release (next) 2023-07-21 08:26:49 +00:00
James Ritchie 7df34db36b Merge remote-tracking branch 'origin/blank-jobs-page-improvements' 2023-07-21 09:25:14 +01:00
James Ritchie 3008909f02 Moved the example jobs list to the left column and only shows when 1 job is visible 2023-07-20 18:37:01 +01:00
D-K-P 8af381729c Added example projects to the blank jobs page 2023-07-20 17:36:23 +01:00
D-K-P 8ca0aeb1bd Updated the side panel copy 2023-07-20 14:48:33 +01:00
Eric Allam 99f8ec935b Fixed pnpm lock 2023-07-20 14:39:42 +01:00
Eric Allam d07cfe8fdb Merge pull request #194 from dylanrod21/main
[TRI-699] using pathToFileURL on path used in detectPagesOrAppDir
2023-07-20 14:37:36 +01:00
Dylan Rodriguez 2f1b2a32c3 adding changeset 2023-07-20 09:09:21 -04:00
D-K-P b6fd62eb54 Changed ‘Send us feedback’ to ‘Help & feedback’ 2023-07-20 12:17:34 +01:00
Eric Allam 1dfe7e0ab5 Updated dev-compose.yml and added some scripts to run it in package.json 2023-07-20 10:31:39 +01:00
Eric Allam 17c7e9765e Merge pull request #197 from SSHari/patch-700
chore: 🤖 improve migrations for docker container startup
2023-07-20 10:31:22 +01:00
Eric Allam 0ae03477df Merge pull request #196 from SSHari/patch-702
feat: 🎸 filter organization > project > jobs to internal=false
2023-07-20 10:11:48 +01:00
Eric Allam 2348e95b6c Merge pull request #195 from SSHari/patch-701
feat: 🎸 add project job count pill to breadcrumb project menu
2023-07-20 10:10:47 +01:00
SSHari 439ecf4a7f chore: 🤖 improve migrations for docker container startup 2023-07-19 21:34:16 -04:00
Dylan Rodriguez 68750da731 Revert "adding where condition on job count"
This reverts commit 61de39712a.
2023-07-19 19:23:40 -04:00
Dylan Rodriguez 61de39712a adding where condition on job count 2023-07-19 19:11:57 -04:00
SSHari d32e6f9839 feat: 🎸 filter organization > project > jobs to internal=false 2023-07-19 19:05:37 -04:00
SSHari 0cda4621c7 feat: 🎸 add project count pill to breadcrumb project menu 2023-07-19 18:48:07 -04:00
Eric Allam 4bcd654e49 Merge pull request #185 from SSHari/patch-573
feat: 🎸 move get project jobs to lower level in app hierarchy
2023-07-19 22:22:37 +01:00
Dylan Rodriguez bf0a0a17a0 normalizing path in detectPagesOrAppDir 2023-07-19 17:14:25 -04:00
SSHari ca59b6b236 Fix requested changes 2023-07-19 12:24:31 -04:00
Eric Allam 2a6cd98d4e Merge pull request #187 from SSHari/patch-445
feat: 🎸 update Docker image to use best practices
2023-07-19 16:56:36 +01:00
Matt Aitken d4da2f322b Added GitHub getIssue Task (with example Job) 2023-07-19 16:31:55 +01:00
SSHari 04ce02bb5d feat: 🎸 update Docker image to use best practices 2023-07-18 16:10:13 -04:00
SSHari 62e3bb93c1 feat: 🎸 move get project jobs to lower level in app hierarchy 2023-07-18 12:10:45 -04:00
Eric Allam 7da6b94daf Add linux header 2023-07-18 16:14:43 +01:00
Eric Allam e95bd3b6fe Rename docker-setup to DOCKER_INSTALLATION and link to our Docker repo 2023-07-18 16:13:42 +01:00
Eric Allam 8e4c70468d Merge pull request #182 from kaf-lamed-beyt/docker-setup
chore(docs): docker-setup guide
2023-07-18 16:11:07 +01:00
Eric Allam 1ea5082314 Use the typesafe env vars instead of process.env directly
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-18 15:43:27 +01:00
Eric Allam 267cbc751c Automatically re-write endpoint url from localhost to host.docker.internal when running in Docker Compose
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-18 14:20:52 +01:00
James Ritchie 5e522e1f3e Updated the yarn install to yarn dlx 2023-07-18 10:51:50 +01:00
Eric Allam ba7ef25bcb Merge pull request #184 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-17 17:20:37 +01:00
github-actions[bot] e132ea33f2 chore: Update version for release (next) 2023-07-17 16:18:09 +00:00
Eric Allam d0b19d50e4 @trigger.dev/cli: Add option to specify the path to the handler function in the dev command 2023-07-17 17:16:24 +01:00
Eric Allam 226ded6156 Merge pull request #183 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-17 16:47:31 +01:00
github-actions[bot] 8f5abfd0a6 chore: Update version for release (next) 2023-07-17 15:47:03 +00:00
Eric Allam 1bf2802b67 @trigger.dev/cli: Parse .env files using dotenv instead of hand-parsing 2023-07-17 16:45:12 +01:00
Eric Allam b95826840a Merge pull request #181 from triggerdotdev/changeset-release/main
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
chore: Update version for release (next)
2023-07-17 16:34:23 +01:00
github-actions[bot] 584e2b3ffb chore: Update version for release (next) 2023-07-17 15:29:22 +00:00
Eric Allam 69af845ad1 Make isRetry context property backwards compatible and add it to the TriggerContext type 2023-07-17 16:27:53 +01:00
Eric Allam fba8eec319 Don’t include application/json Content-Type when pinging with no body (fastify doesn’t like that) 2023-07-17 16:23:50 +01:00
Eric Allam 01c0f9b324 Allow the CLI dev command to work outside of a Next.js project 2023-07-17 16:21:21 +01:00
Matt Aitken 86e8ccbbd6 If the run info is missing then don’t render that part of the breadcrumb
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-17 14:08:35 +01:00
Eric Allam 0b45338300 Remove the bad RESEND_API_KEY from .env.example 2023-07-17 13:00:38 +01:00
kaf-lamed-beyt eafe6a0d7b chore(docs): add docker setup guide. remove prev code implementation 2023-07-17 12:17:09 +01:00
Eric Allam 3fb41f68fb Merge pull request #180 from SSHari/patch-409
feat: 🎸 add live refreshing to Environments & API key page
2023-07-17 11:52:15 +01:00
Eric Allam f5c819a489 Merge pull request #177 from akhill10/feat/isRetry-on-context-run
feat: added isRetry in context.run
2023-07-17 11:48:33 +01:00
Eric Allam 5336317dbb Merge pull request #176 from akhill10/feat/client-define-job
feat: added defineJob method in TriggerClient
2023-07-17 11:40:44 +01:00
kaf-lamed-beyt 6099e06a71 update JobPresenter query to use findFirst. 2023-07-16 15:21:19 +01:00
Akhil 0066971b77 chore: added changeset 2023-07-16 10:36:27 +00:00
Akhil c417a7f9ab feat: added isRetry in jobRunExecution 2023-07-16 07:19:37 +00:00
Akhil 1961b994d7 chore: added changeset 2023-07-16 05:19:30 +00:00
SSHari 5288ca3840 feat: 🎸 add live refreshing to Environments & API key page 2023-07-15 18:24:33 -04:00
Akhil f985dc93aa fix: defineJob with generic for triggerType 2023-07-15 16:30:11 +00:00
Akhil d6e43c40c9 feat: added defineJob method in TriggerClient 2023-07-15 15:55:53 +00:00
kaf-lamed-beyt 184c24cace refactor: use userId instaed of jobId in JobsPresenter 2023-07-15 13:41:44 +01:00
kaf-lamed-beyt 9861cb9aa7 refactor: rewrite JobsPresenter to return a list of Jobs for the project and direct new contributors to the docker setup guide 2023-07-14 19:23:51 +01:00
Matt Aitken 2d722b94bd Latest lockfile 2023-07-14 17:33:24 +01:00
Matt Aitken 2db67e3b4d Merge pull request #171 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-14 17:29:43 +01:00
github-actions[bot] 6290232858 chore: Update version for release (next) 2023-07-14 16:22:31 +00:00
Matt Aitken 7f6bf992a2 Better visibility into webhook registering, fix for Typeform SDK updating webhook 2023-07-14 17:16:28 +01:00
James Ritchie 1f5bd8d6bb Added some copy and a link to let people join the waitlist if they find themselves on cloud.trigger.dev 2023-07-14 16:59:18 +01:00
kaf-lamed-beyt 46383e57d2 chore: update guide 2023-07-14 16:55:00 +01:00
kaf-lamed-beyt 2611b2c171 chore(docs): add docker setup guideline 2023-07-14 16:49:46 +01:00
kaf-lamed-beyt 229207c8f8 chore: create and consume JobPresenter class 2023-07-14 16:40:06 +01:00
Matt Aitken 2eb0b3abce Merge pull request #168 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-14 15:33:44 +01:00
github-actions[bot] 8b6909b486 chore: Update version for release (next) 2023-07-14 14:32:26 +00:00
Matt Aitken dbb9fe44d8 Fix #158 – @trigger.dev/typeform: Check that webhook has a matching url 2023-07-14 15:30:56 +01:00
James Ritchie bb11001fb7 Added 2 buttons below the list of Integrations
2 CTA to request a missing API and to link to the docs to create your own integration
2023-07-13 18:07:19 +01:00
James Ritchie b35457bb53 export type and fixed typo 2023-07-13 18:07:19 +01:00
James Ritchie 1ea0d39fa2 Added an iconClassName to the NamedIcon component so you can override the colour 2023-07-13 18:07:19 +01:00
Matt Aitken 9193b3c90b Merge pull request #157 from triggerdotdev/changeset-release/main
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
chore: Update version for release (next)
2023-07-13 16:38:10 +01:00
github-actions[bot] c349b50164 chore: Update version for release (next) 2023-07-13 15:36:48 +00:00
Matt Aitken fe3bc6efe4 Missing token/apikey error improved for display in Next.js logs 2023-07-13 16:35:21 +01:00
Matt Aitken df138ee96e Latest lockfile 2023-07-13 16:11:37 +01:00
Matt Aitken d0b8e560c9 Merge pull request #153 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-13 16:09:51 +01:00
github-actions[bot] 537409cec1 chore: Update version for release (next) 2023-07-13 14:36:23 +00:00
Matt Aitken bac8ad1040 Throw if the clients are passed an undefined apikey or token 2023-07-13 15:34:35 +01:00
James Ritchie cc26cbeb7b Fixed nintendo related typo 2023-07-13 15:23:37 +01:00
James Ritchie 3b6c2bd0e8 Update CONTRIBUTING.md to include new steps to login when developing locally 2023-07-13 13:50:46 +01:00
Eric Allam 0b004fbcaf Print out emails instead of sending them through resend if missing resend API key 2023-07-13 12:20:54 +01:00
Eric Allam c72c21e2a9 Only retry runs when the response is a non-400 2023-07-13 11:21:14 +01:00
Eric Allam d6310a79fa @trigger.dev/nextjs and @trigger.dev/sdk: Set duplex "half" when creating fetch based Request objects when they have a body 2023-07-12 21:40:13 +01:00
Eric Allam 953354f367 @trigger.dev/typeform: Update how tasks are exported 2023-07-12 21:01:34 +01:00
James Ritchie f8ddb21510 Added env var instructions to CONTRIBUTING.md 2023-07-12 17:53:09 +01:00
Matt Aitken 761e89108b Docker prisma generate uses the same semver as the package.json 2023-07-12 17:35:27 +01:00
James Ritchie ec34a84d6d Removed the Development guide and pointing to CONTRIBUTING.md instead 2023-07-12 16:45:36 +01:00
Eric Allam 9cb5e6847b Update CONTRIBUTING.md
Remove the Next.js prereq
2023-07-12 16:44:28 +01:00
James Ritchie 43f0c6f7f2 Deleted old file 2023-07-12 16:40:52 +01:00
James Ritchie ffcad81498 Deleted old file 2023-07-12 16:40:28 +01:00
James Ritchie f16a337c50 Created a CONTRIBUTING.md guide 2023-07-12 16:39:19 +01:00
Matt Aitken 30e5303c36 Removed eventTrigger.example idea from the example 2023-07-12 16:36:12 +01:00
Matt Aitken 19d5cb596f Merge remote-tracking branch 'origin/triggers-page' 2023-07-12 15:45:54 +01:00
Matt Aitken ff58925606 The Project side menu now shows an error icon on Triggers and Integrations if action is required 2023-07-12 15:44:13 +01:00
Matt Aitken 12b16a0030 Removed unused imports/code 2023-07-12 15:43:54 +01:00
Matt Aitken b08c10a716 Warnings are triangles, errors are circles 2023-07-12 15:43:40 +01:00
Matt Aitken b878561f65 Added custom ErrorIcon that has a white exclamation so it can be seen on busy backgrounds 2023-07-12 15:43:28 +01:00
Eric Allam 5e36ad961f @trigger.dev/cli: create-integration - Generate the README.md file 2023-07-12 15:34:33 +01:00
Matt Aitken 3dc2f0cfff Scheduled Triggers table 2023-07-12 15:06:52 +01:00
Matt Aitken 073c4fb76e TextLink moved to a separate file, and added a secondary style to it 2023-07-12 15:06:46 +01:00
Matt Aitken cef0bd172d Scheduled Triggers tab 2023-07-12 14:43:48 +01:00
Matt Aitken 23eed197ee Don’t show other user’s Dev external triggers 2023-07-12 14:43:33 +01:00
Matt Aitken 3f8f76df76 Interval Trigger icon is a clock, instead of a calendar to differentiate from CRON 2023-07-12 14:43:16 +01:00
Matt Aitken b047023d9c When registering a ScheduleSource you can associate a DynamicTrigger 2023-07-12 14:43:02 +01:00
Matt Aitken 420655f231 Database migration: relationship between ScheduleSource and DynamicTrigger 2023-07-12 14:42:35 +01:00
Eric Allam 520ab88a97 Adding the create-integration command to scaffold out new integration packages (inside or outside of the monorepo) 2023-07-12 13:38:43 +01:00
Eric Allam f96da50e6a Update the typeform integration package
Now uses a dependency free tsconfig file, and updated the typescript and tsup dev deps
2023-07-12 13:35:19 +01:00
Matt Aitken 1dc42daed4 Added SDK and API support to stop runs when they’re canceled 2023-07-12 13:14:23 +01:00
Matt Aitken 0a80e2f5ec Added CANCELED TaskStatus to the server 2023-07-12 09:18:59 +01:00
Matt Aitken 4661d2019c Canceling runs 2023-07-11 19:50:06 +01:00
Matt Aitken 4e4e7b705d Rerun moved to a resource route 2023-07-11 18:49:00 +01:00
Matt Aitken bae1b0be72 Added action to retry registration 2023-07-11 18:06:42 +01:00
Matt Aitken e890ee01a3 renamed pathBuilder functions to external 2023-07-11 18:06:36 +01:00
Matt Aitken af4b0757a9 Improved spacing and copy 2023-07-11 16:30:39 +01:00
Matt Aitken 6ef62d82c4 You can add containerClassName to a Table 2023-07-11 16:27:05 +01:00
Matt Aitken 44df509b62 Added a code comment to explain the complicated breadcrumb for the extrnal trigger page 2023-07-11 15:56:18 +01:00
Matt Aitken 5d3fe2a5ea Changed paths from “triggersources” to “external” 2023-07-11 15:53:15 +01:00
Matt Aitken 076e6dce73 Cloud seed updates existing secret store values 2023-07-11 15:35:04 +01:00
Matt Aitken f745320456 Added DynamicTrigger column 2023-07-11 14:46:26 +01:00
Matt Aitken 6e64aaa1ee Better explanations of triggers 2023-07-11 14:46:21 +01:00
Matt Aitken 8b68edfc05 Get only the required DynamicTrigger info 2023-07-11 14:46:06 +01:00
Matt Aitken 0a7bae4dac Use the handle to determine if the side menu is expanded 2023-07-11 14:35:14 +01:00
Matt Aitken b925a573f3 Added paginated runs 2023-07-11 13:24:38 +01:00
Eric Allam 0012bb21ce All logs are now structured logs 2023-07-11 13:11:30 +01:00
Matt Aitken 3b3ec9fc4a Run streaming logic moved to a presenter, shared with the External Trigger run page 2023-07-11 13:04:56 +01:00
Matt Aitken 396663d65c Hooks can use passed in matches. Breadcrumb updated to be in the route 2023-07-11 12:46:39 +01:00
Eric Allam f275b47755 Fixed pnpm lock 2023-07-11 11:31:42 +01:00
Eric Allam cfb7481d7e Merge pull request #152 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-11 11:22:55 +01:00
github-actions[bot] 57fcce90d9 chore: Update version for release (next) 2023-07-11 10:22:13 +00:00
Eric Allam 2c0ea0c108 Published packages now work with 16.8 and above 2023-07-11 11:20:47 +01:00
Eric Allam e81317b72c Loosen the @trigger.dev/nextjs next version 2023-07-11 11:04:21 +01:00
Matt Aitken 686cf9dc7e Trigger registration runs displaying 2023-07-10 16:35:48 +01:00
Matt Aitken a9e7cda37d Run page turned into components so it can be shared 2023-07-10 15:47:13 +01:00
Matt Aitken eae0b8a95e The Run page now uses a component so it can be shared 2023-07-10 15:20:01 +01:00
Matt Aitken 1b65b1d596 Deleted legal pages from the app 2023-07-10 15:19:45 +01:00
Matt Aitken d85b1fe2fd Moved components to the components/run folder ready for sharing 2023-07-10 15:02:03 +01:00
Matt Aitken f00006af1b Run summary title made the same size as the others 2023-07-10 14:58:01 +01:00
Matt Aitken 75a07fe4ef RunsTable displaying for registration attempts 2023-07-10 14:40:10 +01:00
James Ritchie 946e429906 Merge pull request #151 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-10 13:44:20 +01:00
github-actions[bot] 2e5a401ff7 chore: Update version for release (next) 2023-07-10 12:42:38 +00:00
James Ritchie b8b84d8d16 CLI: Improved the endpoint prompt 2023-07-10 13:41:04 +01:00
Matt Aitken 4373538476 ClipboardField changed to spans so it can work inside a paragraph 2023-07-10 12:24:11 +01:00
Matt Aitken af79ea2f72 Triggers page now has tabs 2023-07-10 11:49:19 +01:00
Matt Aitken aab859bc20 Added scheduled path 2023-07-10 11:48:58 +01:00
Matt Aitken c692c16183 Trigger red table row and added slug to the Integration 2023-07-10 11:07:08 +01:00
Matt Aitken cb59808be2 Better type for LabelValueStack variations 2023-07-10 11:06:51 +01:00
Matt Aitken 039777bf63 Merge pull request #150 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-10 10:59:15 +01:00
github-actions[bot] 3547ec13c3 chore: Update version for release (next) 2023-07-10 09:58:47 +00:00
Matt Aitken 3763577451 CLI dev: improved spinner and made port clear in logs 2023-07-10 10:57:10 +01:00
Matt Aitken c6618049f2 Merge pull request #149 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-10 10:35:07 +01:00
github-actions[bot] 28d6aa9885 chore: Update version for release (next) 2023-07-10 09:33:29 +00:00
Matt Aitken 568786e4f6 Fix when setting the url as an option 2023-07-10 10:32:07 +01:00
Matt Aitken 2aad2d29de Early work on Trigger detail page 2023-07-10 10:16:44 +01:00
Matt Aitken 400354ee9e Improved the layout 2023-07-09 21:12:46 +01:00
Matt Aitken ea3ba78d1a Started the Triggers page 2023-07-09 20:56:33 +01:00
Eric Allam 38614ee526 Update pnpm lockfile 2023-07-09 20:43:27 +01:00
Eric Allam 15ad6bb5f8 Merge pull request #148 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-09 20:41:41 +01:00
github-actions[bot] 15f431ada6 chore: Update version for release (next) 2023-07-09 18:48:48 +00:00
Matt Aitken 5163acc6ef Plain changed to @latest
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-09 19:47:32 +01:00
Matt Aitken f9297d783a Added Plain to the integration catalog 2023-07-08 22:17:58 +01:00
Eric Allam c34a02c06f Improved OpenAI integration task errors
And also now allowing runTask.onError to also return an Error by itself if it wants to not retry the task
2023-07-08 22:14:27 +01:00
Eric Allam 61ed1fb202 Added support for output properties on tasks 2023-07-08 22:14:27 +01:00
Eric Allam 87638ba6c5 Merge pull request #147 from owen-duncan-snobel/main
Update README.md
2023-07-07 21:51:36 +01:00
owen-duncan-snobel 26b4100bb0 Update README.md
Use the permanent link to the discord
2023-07-07 15:26:34 -04:00
Matt Aitken 1157c2adf6 Merge pull request #144 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-07 19:05:26 +01:00
github-actions[bot] 8613d3288b chore: Update version for release (next) 2023-07-07 18:05:13 +00:00
Matt Aitken ab008fbdff Typeform example formId is optional 2023-07-07 19:03:17 +01:00
Matt Aitken 0b30197280 Set schema for passing in the formId 2023-07-07 19:03:17 +01:00
Matt Aitken 89a121f21e Typeform SDK getAllResponses fixed 2023-07-07 19:03:17 +01:00
Matt Aitken 045a8c56af Subtasks are now ordered correctly 2023-07-07 19:03:17 +01:00
Eric Allam d83c11db8c Add back in npm-run-all
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-07 16:39:29 +01:00
Eric Allam 7efe14b835 Logging in via github should work even after changing your email address 2023-07-07 16:34:41 +01:00
Matt Aitken 37116aecf9 Latest lockfile 2023-07-07 15:37:51 +01:00
Matt Aitken 9a606e8f2a Merge pull request #143 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-07 15:35:00 +01:00
github-actions[bot] b1e65ed500 chore: Update version for release (next) 2023-07-07 14:34:41 +00:00
Matt Aitken 26f3dbff06 Typeform: safeBodyParse webhook import fixed 2023-07-07 15:32:45 +01:00
Eric Allam 5ee0b188bd Improve the error display when a run cannot connect to an endpoint 2023-07-07 15:32:19 +01:00
Matt Aitken 4692cd6a55 Latest lockfile 2023-07-07 15:20:42 +01:00
Matt Aitken 7ad8c01853 Merge pull request #142 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-07 15:19:13 +01:00
github-actions[bot] bc27b45fd7 chore: Update version for release (next) 2023-07-07 14:18:55 +00:00
Matt Aitken 94cc28d377 Changeset: integration-kit uses webhook package 2023-07-07 15:17:32 +01:00
Matt Aitken 19e80cef3b Removed “internal-queue” from indexEndpoint 2023-07-07 15:16:18 +01:00
James Ritchie 5397cd3194 Added environment labels to the enironment and api keys help panel 2023-07-07 14:51:44 +01:00
Eric Allam f01af9c0ec Remove a bunch of unused webapp deps and upgrade packages to zod 3.21.4 2023-07-07 14:47:28 +01:00
James Ritchie a288010d02 Added a new help panel to the api and endpoints page 2023-07-07 14:20:35 +01:00
James Ritchie b9a7a1c32b Label component now uses the correct <label> html element 2023-07-07 14:20:35 +01:00
James Ritchie 65b9a0dce5 Improved the Header component to make it easier to add more props 2023-07-07 14:20:35 +01:00
Eric Allam 7464006e19 Associated trigger sources and dynamic triggers with their registration job 2023-07-07 13:47:19 +01:00
Matt Aitken 590d56b7e7 Merge pull request #141 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-07 12:18:57 +01:00
github-actions[bot] c75cb1e87e chore: Update version for release (next) 2023-07-07 11:16:51 +00:00
Matt Aitken 597b2f4a6f Plain example Job 2023-07-07 12:15:16 +01:00
Matt Aitken 2e84c6bbb2 Plain: Added upsertCustomTimelineEntry Task and exported the components from the plain package 2023-07-07 12:15:16 +01:00
Matt Aitken 06c0cd5297 Import plain jobs file so it gets run, also alphabetized them 2023-07-07 12:15:16 +01:00
Eric Allam 79eab3d62f Cleanup unused graphile tasks 2023-07-07 11:46:56 +01:00
Eric Allam 6527723a0e Refactor index endpoint to work sequentially so we can add associations between sources and jobs 2023-07-07 11:36:01 +01:00
Eric Allam 3d6652f132 WIP plain integration 2023-07-07 10:59:36 +01:00
Matt Aitken da5d9779d4 Check if the Highlight id is defined before attempting to send the error 2023-07-06 21:03:20 +01:00
Matt Aitken 865399e753 Enabled tracing so we can track down errors between frontend and backend 2023-07-06 20:56:50 +01:00
Matt Aitken 960ea6c8dd Removed the throw button from the NavBar… obvs should never have been committed ;) 2023-07-06 20:56:35 +01:00
Matt Aitken 39ab48f0cb Highlight server-side error tracking 2023-07-06 20:45:20 +01:00
Matt Aitken 7341b4cf61 Highlight identifies users 2023-07-06 20:45:00 +01:00
Matt Aitken 28c5acc6af Added Highlight to the project, with basic client-side tracking 2023-07-06 19:34:53 +01:00
Matt Aitken 4366eefc66 Removed unused import 2023-07-06 19:34:05 +01:00
Matt Aitken cb95e761c1 Added an error boundary to the _app route 2023-07-06 19:33:32 +01:00
Matt Aitken 7136748463 Added an error boundary to org 2023-07-06 19:29:42 +01:00
Matt Aitken b0cacfd75c Added an error boundary to project 2023-07-06 19:29:32 +01:00
Matt Aitken 81f03dac79 Better error when a job isn’t found 2023-07-06 19:29:17 +01:00
Matt Aitken e37d1eadcd Breadcrumb null checks in case error boundaries are hit deep in the app 2023-07-06 19:29:00 +01:00
Matt Aitken 23fb310374 Reusable ErrorDisplay 2023-07-06 19:28:36 +01:00
Matt Aitken 797a1732d2 Organize imports on ProjectSideMenu 2023-07-06 19:08:06 +01:00
Matt Aitken c405a417e9 If an error boundary is hit the meta function can return undefined… 2023-07-06 19:07:46 +01:00
James Ritchie 808deb1d77 Added data attributes to the side menu items 2023-07-06 17:50:44 +01:00
Eric Allam 6ac1473ec3 Merge pull request #139 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-06 15:31:53 +01:00
github-actions[bot] 9c183abf4b chore: Update version for release (next) 2023-07-06 14:27:36 +00:00
Eric Allam 33c33faae9 Adding the typeform integration to the catalog 2023-07-06 15:26:02 +01:00
Matt Aitken 4cccbc2039 PostHog updated to change hooks, also added “job” group 2023-07-06 14:58:29 +01:00
Matt Aitken cc061dedf8 Change hooks for User, Org, Project, Job 2023-07-06 14:58:29 +01:00
Matt Aitken 402ce2210f New hook that lets you trigger an action when an object changes 2023-07-06 14:58:29 +01:00
Matt Aitken 329c5c4070 Updated posthogjs 2023-07-06 14:58:29 +01:00
Matt Aitken 878aeb7a8c useOrganizationChanged 2023-07-06 14:58:29 +01:00
Eric Allam 3ee396d788 Adding the typeform integration with webhook support 2023-07-06 13:42:49 +01:00
Eric Allam 090037602c WIP create-integration CLI command 2023-07-06 13:42:49 +01:00
Matt Aitken bee77051e7 Added UI to guide you to fixing unconfigured integrations 2023-07-06 11:56:19 +01:00
Matt Aitken be6741a4a8 Example Job that has an unconfigured Integration (for testing) 2023-07-06 11:56:19 +01:00
Matt Aitken 3e9b440322 NamedIcon uses the new type 2023-07-06 11:56:19 +01:00
Matt Aitken dfea5bc77c The connection-alert trigger type has an amber icon so it doesn’t look like an error 2023-07-06 11:56:19 +01:00
Matt Aitken 1eef30e298 Callout uses to and supports internal and external links 2023-07-06 11:56:19 +01:00
James Ritchie e5b9a9de3f Noun’d up Job 2023-07-06 11:36:13 +01:00
Matt Aitken dfedd3a6e8 Consistently use “Log in” everywhere 2023-07-06 10:08:09 +01:00
Matt Aitken a19845b4ca Added a jsdoc markr to the source id 2023-07-06 09:32:15 +01:00
Matt Aitken 094f6f5a7a jsdocs for DynamicTrigger and DynamicSchedule changeset 2023-07-06 00:37:01 +01:00
Matt Aitken fa60edce2b jsdocs for DynamicTrigger and DynamicSchedule 2023-07-06 00:36:08 +01:00
Matt Aitken ba44652457 intervalTrigger() jsdocs 2023-07-05 20:17:09 +01:00
Matt Aitken 931be39912 cronTrigger jsdocs 2023-07-05 19:32:35 +01:00
James Ritchie 5048a27acb Capital J for Job 2023-07-05 18:27:04 +01:00
Matt Aitken 71fc615b31 Fix for the profile photo sizing being weird 2023-07-05 18:05:41 +01:00
Eric Allam 20ed4e22ef Implement the cloud private beta invite form
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-05 17:27:59 +01:00
Matt Aitken fc2d337a39 Optional trailing icon on a <TextLink> 2023-07-05 17:22:53 +01:00
Matt Aitken 31d96f3862 External icon uses the small version 2023-07-05 17:22:44 +01:00
Matt Aitken 7b43853ec6 Help content for the Job page 2023-07-05 17:11:45 +01:00
Matt Aitken 40fa913272 TextLink can have a to or an href 2023-07-05 17:10:34 +01:00
Matt Aitken cdd5482aad Capitalized Runs 2023-07-05 17:10:17 +01:00
Matt Aitken 4a3fa20ee7 Export the callout styles 2023-07-05 17:10:10 +01:00
Matt Aitken d49ed4cd47 Fix for div inside paragraph error 2023-07-05 17:09:53 +01:00
James Ritchie db3cf77f5b Updated the pagination buttons style 2023-07-05 17:08:35 +01:00
James Ritchie c4a08961d6 Added a Help Panel to the Jobs list page 2023-07-05 17:08:35 +01:00
James Ritchie 5ba1643d46 Added a new qr code named icon 2023-07-05 16:57:33 +01:00
Eric Allam d189f425e8 Limit access to the cloud product to people who have invite codes 2023-07-05 16:31:41 +01:00
Matt Aitken 0fe6fb83ac The test help panel is open when you load the page 2023-07-05 16:09:20 +01:00
Matt Aitken f15ae41f48 Help lightbulb is always yellow 2023-07-05 16:09:20 +01:00
Eric Allam 46a07fc72e Merge pull request #138 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-05 15:59:30 +01:00
github-actions[bot] 83af317739 chore: Update version for release (next) 2023-07-05 14:57:17 +00:00
Eric Allam f4db1b2735 @trigger.dev/cli init - Create a better file structure 2023-07-05 15:54:58 +01:00
Eric Allam 6d4922f419 api.trigger.dev is now the default cloud url 2023-07-05 14:47:41 +01:00
Eric Allam 6af098ee8a Default the apiUrl to cloud.trigger.dev 2023-07-05 12:29:48 +01:00
Eric Allam 1dcb2ebcac Update pnpm lock file 2023-07-05 12:25:10 +01:00
Eric Allam f65cb463cc Merge pull request #137 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-05 12:20:43 +01:00
github-actions[bot] 2b4ce97f8a chore: Update version for release (next) 2023-07-05 11:12:37 +00:00
Eric Allam 25abf319ab PageInfoProperty not puts the value inside a paragraph even if it’s a react node
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-05 12:11:12 +01:00
James Ritchie 77807ab42a Changed ‘Clients’ to ‘Endpoints’ on the API keys page and reordered URL in table 2023-07-05 11:38:06 +01:00
Eric Allam f21990b14f Removed the datetime logging
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-05 11:36:44 +01:00
Eric Allam 1873d10e95 Normalize the whitespace characters in formatted date times 2023-07-05 11:13:26 +01:00
Matt Aitken 32ff3f3136 @trigger.dev/github: Fixed some properties that had API urls instead of HTML urls 2023-07-05 11:12:27 +01:00
Matt Aitken 354b0aefb0 “This Run returned nothing.” 2023-07-05 10:59:37 +01:00
Matt Aitken aaa70a9aab eventTrigger() jsdocs 2023-07-05 10:59:37 +01:00
Eric Allam 3b90739087 Rework the DateTime component to use a Fragment and render the correct datetime timezone in a useEffect 2023-07-05 10:51:08 +01:00
Matt Aitken acaae993a5 Run context jsdocs 2023-07-04 22:55:49 +01:00
Eric Allam a74f0322e2 Attempt to fix the prisma “Transaction already closed: Could not perform operation” error
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
- Removes the transaction stuff from StartRunService (can try putting it back more granularly)
- Uses $transaction in TestJobService
- Better queue names for the startRun and deliverEvent worker job
2023-07-04 22:11:53 +01:00
Matt Aitken 1a0286f6b0 Run pnpm i 2023-07-04 18:52:54 +01:00
Matt Aitken 64182483e2 Merge pull request #136 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-04 18:37:46 +01:00
github-actions[bot] 87a25fba62 chore: Update version for release (next) 2023-07-04 17:34:44 +00:00
Matt Aitken 12803536fa OpenAI integration: listModels returns the response.data instead of response.data.data 2023-07-04 18:33:27 +01:00
Matt Aitken f160b34b34 isTriggerError jsdocs 2023-07-04 16:28:58 +01:00
Matt Aitken cca7da9da2 Better docs for io.try 2023-07-04 16:15:47 +01:00
Matt Aitken 01cf5f3bdc io.try jsdocs 2023-07-04 16:14:31 +01:00
Matt Aitken 33a928a9ea Removed new job examples from general 2023-07-04 15:30:56 +01:00
Matt Aitken c83443a41a io.runTask and io.registerTrigger jsdocs 2023-07-04 15:30:06 +01:00
Eric Allam 374007bffd Fixed incorrectly scoping job test runs to the specific selected project
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-07-04 15:23:19 +01:00
Eric Allam 092382f319 Cleanup some of the date times 2023-07-04 15:14:45 +01:00
Eric Allam 79a0532e1d Handle OS signals in the docker container
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
2023-07-04 14:36:59 +01:00
Eric Allam 56c7c8b1ee Better handle interactive transaction errors and adding increased timeouts 2023-07-04 14:36:59 +01:00
Matt Aitken 722fe7b71e registerCron and unregisterCron jsdocs 2023-07-04 12:48:32 +01:00
Eric Allam 759793b2e3 Fix hydration errors by using the LocaleProvider for all rendered dates, and always use UTC to prevent timezone mismatches 2023-07-04 12:36:42 +01:00
Matt Aitken 99c6cd0371 io.registerInterval and io.unregisterInterval jsdocs 2023-07-04 11:22:28 +01:00
Eric Allam 9fbc5ab8de Adding logging to investigate hydration errors 2023-07-04 11:09:47 +01:00
Eric Allam 75ac2c9961 Scheduled triggers are now disabled on dev env by default
Also added pk api key to the environments now
2023-07-04 10:59:05 +01:00
Eric Allam 87330dd863 Merge pull request #135 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-04 10:50:18 +01:00
github-actions[bot] 38fe9302cf chore: Update version for release (next) 2023-07-04 09:36:25 +00:00
Eric Allam 33a455c593 pnpm i 2023-07-04 10:34:29 +01:00
Eric Allam 37db0ac58d Remove @trigger.dev/internal changeset 2023-07-04 10:33:00 +01:00
Eric Allam 2cbf50b1cc @trigger.dev/sdk: deliverAt and timestamp event properties are now dates 2023-07-04 10:32:08 +01:00
Matt Aitken e26923eb19 backgroundFetch jsdocs 2023-07-04 10:31:44 +01:00
Eric Allam 3e63bcfb11 Merge pull request #134 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-03 22:50:55 +01:00
github-actions[bot] 1e1c18c4a1 chore: Update version for release (next) 2023-07-03 21:49:48 +00:00
Eric Allam 953e7fc918 Allow scheduled jobs to have their schedule updated
Also added human readable cron property to cron triggers
2023-07-03 22:48:03 +01:00
Matt Aitken 8e147dbe0c io.sendEvent jsdocs 2023-07-03 19:19:56 +01:00
Matt Aitken 86dbd5d1dc Added JSdocs for io.wait and io.logger 2023-07-03 19:02:58 +01:00
Eric Allam e85ecfacba Remove the commented out jobs 2023-07-03 17:49:00 +01:00
Eric Allam f210532870 @trigger.dev/slack: Added joinConversation task, and automatically try and join a public conversation if the bot is not in it when posting a message 2023-07-03 17:48:14 +01:00
James Ritchie 6c9ca686cb Tweaked callout icon margin 2023-07-03 16:39:21 +01:00
James Ritchie 35b556d4a3 Using the path builder for URLs on the Jobs page 2023-07-03 16:39:21 +01:00
James Ritchie c83339404a Using the path builder for the integration docs URL 2023-07-03 16:39:21 +01:00
James Ritchie 4172def1a3 Added a docs root path to the path builder 2023-07-03 16:39:21 +01:00
James Ritchie c99481ea8d Added a callout to link to the docs 2023-07-03 16:39:21 +01:00
Matt Aitken a11ddf6564 Added JSDocs related to logging 2023-07-03 16:21:14 +01:00
Matt Aitken 917a70fbe9 Docs for the Job constructor() 2023-07-03 16:12:21 +01:00
Eric Allam 486d6818ba SDK: IO Logging now respects the job and client logLevel, and only outputs locally when ioLogLocalEnabled is true 2023-07-03 16:00:18 +01:00
Matt Aitken 807b9d4c45 SDK changeset: Added jsdocs for TriggerClient() and sendEvent() 2023-07-03 15:37:36 +01:00
Matt Aitken 0013e78b1a CLI changeset: Export the client from the generated code and added code comments 2023-07-03 15:37:25 +01:00
Matt Aitken 9dd46c83fd Added code comments to the CLI generated code, and exported the client 2023-07-03 15:35:40 +01:00
Matt Aitken 1eb6bb8aa9 Added JSDocs for TriggerCient() and client.sendEvent() 2023-07-03 15:35:40 +01:00
Eric Allam 6c86946614 SDK: Fixes issue with PING response
CLI: improves how the dev command handles register errors, also doesn’t create a tunnel when hitting local trigger.dev, and improves the dev command output
2023-07-03 15:33:44 +01:00
Eric Allam 612b016a15 When registering a schedule source < 60 seconds, make it 60 seconds 2023-07-03 14:00:46 +01:00
Eric Allam be8beacaec chore: update to Remix 1.18.1 2023-07-03 13:53:38 +01:00
Eric Allam 4d316c5524 Fixed the OAuth2 callback urls by constructing URL using x-forwarded-proto header 2023-07-03 13:47:58 +01:00
Eric Allam 7fee5d6a87 Remove logging of headers and replace integration list date with DateTime component 2023-07-03 13:26:53 +01:00
Eric Allam 0dd1aa5a5b Log out the server locale context 2023-07-03 13:11:32 +01:00
Eric Allam ed65ba4a73 isManagedCloud should be true when in the test cloud 2023-07-03 11:53:23 +01:00
Matt Aitken d39020370c Merge pull request #133 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-07-03 11:43:28 +01:00
github-actions[bot] c18ba11819 chore: Update version for release (next) 2023-07-03 10:29:42 +00:00
Matt Aitken 5fbd7226eb Changeset: @trigger.dev/cli: Added retrying and better messaging 2023-07-03 11:28:22 +01:00
Matt Aitken c9e27fd586 Improved the loading state of the CLI dev command. Fail after 10 connection attempts 2023-07-03 11:27:36 +01:00
Matt Aitken 51b80eb09b Deleted old CLI createProject file 2023-07-03 11:27:22 +01:00
Matt Aitken f409e9f329 Improved the messaging when it’s connecting with a retry 2023-07-02 18:54:25 +01:00
Matt Aitken 304e6aaad5 Only triggers a refresh when a relevant file is changed, retries with exponential backoff 2023-07-02 18:45:34 +01:00
Eric Allam 3c5d02043e Merge pull request #132 from triggerdotdev/changeset-release/main 2023-06-30 22:07:19 +01:00
github-actions[bot] 63980f63d4 chore: Update version for release (next) 2023-06-30 18:45:02 +00:00
Eric Allam facd2b2131 Update lockfile 2023-06-30 19:43:20 +01:00
Matt Aitken 25452588cc Update the sdk version in peer dependencies of @trigger.dev/nextjs 2023-06-30 18:05:53 +01:00
Matt Aitken 195d8c42fc Updated posthog-js to the latest 2023-06-30 18:02:03 +01:00
Matt Aitken fe04b9d32d Next.js changeset: adaptors no longer take an options object 2023-06-30 17:44:55 +01:00
Matt Aitken d03b3aeb5f Latets lockfile 2023-06-30 17:29:04 +01:00
Matt Aitken 2ee33e70f7 Fixed Slack help line highlighting 2023-06-30 17:25:57 +01:00
Matt Aitken e9209629f7 Show contextual help for an Integration 2023-06-30 17:24:55 +01:00
Matt Aitken fa1e6bf5e6 The new fields from the Integration catalog are now in the database 2023-06-30 17:24:40 +01:00
Matt Aitken 93c3b10bf7 Improved the help text some more
🚀 Publish Trigger.dev Docker / ʦ TypeScript (push) Has been cancelled
🚀 Publish Trigger.dev Docker / publish (push) Has been cancelled
2023-06-30 15:43:45 +01:00
Matt Aitken 56cb2e3b07 Updated the in-app quick start instructions 2023-06-30 15:38:40 +01:00
Eric Allam eabfb2457a Remove pushing to ECR 2023-06-30 15:35:20 +01:00
Eric Allam 317ab442ee Merge pull request #131 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-06-30 15:12:43 +01:00
github-actions[bot] 2b24b36da6 chore: Update version for release (next) 2023-06-30 14:10:09 +00:00
Eric Allam 161d59d01d Added better Next Steps in the init cli command 2023-06-30 15:07:46 +01:00
Eric Allam 821b75ed26 Merge pull request #130 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-06-30 14:30:38 +01:00
github-actions[bot] 1f901e68d3 chore: Update version for release (next) 2023-06-30 13:27:31 +00:00
Eric Allam 46d94a6a8f Fixed type errors 2023-06-30 14:26:07 +01:00
Eric Allam 44c2146505 Merge pull request #129 from triggerdotdev/changeset-release/main
chore: Update version for release (next)
2023-06-30 14:14:32 +01:00
Matt Aitken ec2f1f5931 Support for Integrations that are incomplete on the integrations page 2023-06-30 14:13:23 +01:00
Matt Aitken e86c59f4ec If logging is on show Posthogs logs 2023-06-30 14:13:23 +01:00
James Ritchie 6df6813a17 Styled scroll bar for the test json 2023-06-30 14:06:10 +01:00
James Ritchie f73679e2a6 Updated help image 2023-06-30 14:06:10 +01:00
Eric Allam 68b866921a Remove triggering a test deploy on publishing 2023-06-30 13:48:08 +01:00
James Ritchie 35364d615a Custom scrollbars now on the Job and Project navbar menus 2023-06-30 13:18:13 +01:00
Matt Aitken f64dd2a99c Added nice scrollbar, commented out missing integration stuff for now 2023-06-30 12:56:10 +01:00
Matt Aitken a309a75a4d NamedIcon now gives autocomplete in the editor (but also accepts any string) 2023-06-30 12:18:19 +01:00
Matt Aitken c2e2f7cad9 CLI: removed —tunnel param as we currently just support ngrok 2023-06-30 12:18:19 +01:00
Matt Aitken f69b92b153 Uncommented the jobs 2023-06-30 12:18:19 +01:00
Matt Aitken f8760fdd6b Moved the jobs so they’re imported and can be easily commented out 2023-06-30 12:18:19 +01:00
Matt Aitken caa4daabe2 Use the id as the temporary name 2023-06-30 12:18:19 +01:00
github-actions[bot] 67f685af26 chore: Update version for release (next) 2023-06-30 11:11:54 +00:00
Eric Allam b2a8b5653f Adding changeset for the new dev command 2023-06-30 12:10:30 +01:00
Eric Allam 9232f019e1 Fixed a couple type errors in the @trigger.dev/cli 2023-06-30 11:29:25 +01:00
Eric Allam ac371664af Better handle input for the --trigger-url option 2023-06-30 11:26:46 +01:00
Matt Aitken e4cca81124 Integrations page: added a Connections column and deal with local auth better 2023-06-30 11:11:28 +01:00
Matt Aitken d76d2b86d2 Indexing endpoints happens in a transaction 2023-06-30 11:11:28 +01:00
Matt Aitken fcd2d6eea2 Integrations now have a setupStatus and can be missing fields 2023-06-30 11:11:28 +01:00
Matt Aitken 067064d19c Include Slack jobs 2023-06-30 11:11:28 +01:00
Matt Aitken 52cdfb98a4 Added extra error checking to the refresh endpoint call 2023-06-30 11:11:28 +01:00
Eric Allam 41261afcc4 Fix the pnpm lock file AGAIN 2023-06-30 11:10:13 +01:00
James Ritchie 77f71fdeb7 WIP adding 2 links below the api list 2023-06-30 11:09:15 +01:00
James Ritchie ce0032e7e5 Minor copy tweaks 2023-06-30 10:33:05 +01:00
Matt Aitken c97ca350ad The endpoint gets automatically refreshed when files are changed, with a throttle 2023-06-29 22:19:55 +01:00
Matt Aitken 90abcb2cf7 Improved messages 2023-06-29 21:53:16 +01:00
Matt Aitken 8626172faa Installed packages 2023-06-29 21:53:09 +01:00
Matt Aitken 44ef97c656 Set the nextjs-example packages to workspace again 🤦‍♂️ 2023-06-29 21:53:05 +01:00
Matt Aitken 7661fe5309 CLI dev command is creating a tunnel and attempting to connect to the API 2023-06-29 21:29:28 +01:00
Matt Aitken beb02c871d Added the CLI to the nextjs-example project 2023-06-29 21:12:21 +01:00
Matt Aitken 8a47320272 WIP on dev CLI command 2023-06-29 20:32:07 +01:00
Matt Aitken 114c8990a3 Change the main header to h1 to stop eslint complaining 2023-06-29 19:34:00 +01:00
Eric Allam 7e2d48ac6a Adding SDK changeset for trigger client url 2023-06-29 18:26:53 +01:00
Eric Allam dbb6667889 Couple of changes for matt 2023-06-29 18:25:31 +01:00
Eric Allam 29a02131d2 WIP converting the init into a multi-command CLI to support dev 2023-06-29 18:15:19 +01:00
James Ritchie 5d887f1cfb Improved the sheet style 2023-06-29 17:29:46 +01:00
James Ritchie 18f0e73ed4 Added yarn and pnpm options for installing the package 2023-06-29 16:41:19 +01:00
James Ritchie c4c1425c9b WIP making the 2 styles of dropdowns similar. Bare minimum for now for launch. 2023-06-29 15:55:15 +01:00
James Ritchie 00bb3934ad import cleanup 2023-06-29 15:27:59 +01:00
James Ritchie 324096e14e typo 2023-06-29 15:27:48 +01:00
James Ritchie 9cad2bb308 scopes title style update 2023-06-29 15:15:13 +01:00
James Ritchie eec537135a fixed radio button alignment 2023-06-29 15:13:04 +01:00
James Ritchie 53e42d3350 Integration page layout now uses 2 and 1 col grid layout 2023-06-29 15:01:00 +01:00
James Ritchie e74122977a logo alignment fix 2023-06-29 15:00:10 +01:00
James Ritchie f32c3ebe2c higher res help images 2023-06-29 14:48:58 +01:00
James Ritchie d07ac4fef0 Added a callout link to the docs 2023-06-29 14:42:49 +01:00
James Ritchie 183761b01f Aligned the callout icon top 2023-06-29 14:42:39 +01:00
James Ritchie 361ef70ce8 api key fields not fullwidth 2023-06-29 14:42:31 +01:00
James Ritchie 2bac0a4e5b Added content to the Test help panel and made the -t dynamic 2023-06-29 14:35:34 +01:00
James Ritchie b89a68bb9c Added a hook for appOrigin 2023-06-29 14:35:34 +01:00
James Ritchie 496dadebbd Fixed the page layout to work with 1 or 2 col grid layout 2023-06-29 14:35:34 +01:00
862 changed files with 40540 additions and 17492 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Adding support for trigger source in the run context, and make sure dynamic trigger runs are preprocessed so they have a chance of populating run properties
-9
View File
@@ -1,9 +0,0 @@
---
"@trigger.dev/init": patch
---
A number of improvements to the initialization experience:
- Detect middleware usage and warn about how it could conflict with Trigger.dev and with a link to the docs
- Lookup package versions and use the latest versions
- Better error messages when registering an endpoint doesn't work
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/github": patch
---
Fixed the broken export issue with two of the new tasks
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Fix for Next.js projects using the src dir
+3 -6
View File
@@ -2,13 +2,10 @@
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"fixed": [["@trigger.dev/*"]],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [
"webapp",
"emails"
]
}
"ignore": ["webapp", "emails", "@trigger.dev/database"]
}
-11
View File
@@ -1,11 +0,0 @@
---
"@trigger.dev/integration-kit": patch
"@trigger.dev/sdk": patch
"@trigger.dev/github": patch
"@trigger.dev/openai": patch
"@trigger.dev/slack": patch
"@trigger.dev/nextjs": patch
"@trigger.dev/init": patch
---
Creating the init CLI package
-8
View File
@@ -1,8 +0,0 @@
---
"@trigger.dev/sdk": major
"@trigger.dev/github": major
"@trigger.dev/slack": major
"@trigger.dev/nextjs": major
---
Preparing packages for V2
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Remove the localhost default for endpoint URL
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Fixed the version of the @trigger.dev/nextjs package
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Endpoint registration and indexing now is only initiated outside of clients
-7
View File
@@ -1,7 +0,0 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/github": patch
"@trigger.dev/nextjs": patch
---
Add support for task errors and task retrying
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Improved the CLI prompts
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Fixed the eventTrigger name
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Adding more granular error messages around unauthorized requests
-8
View File
@@ -1,8 +0,0 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/github": patch
"@trigger.dev/openai": patch
"@trigger.dev/slack": patch
---
Added support for backgroundFetch
-42
View File
@@ -1,42 +0,0 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"webapp": "1.0.0",
"@trigger.dev/github": "0.2.0",
"@trigger.dev/slack": "0.2.0",
"create-trigger": "0.2.0",
"emails": "1.0.0",
"@trigger.dev/internal": "0.0.1",
"@trigger.dev/nextjs": "0.1.0",
"@trigger.dev/sdk": "1.0.0",
"@trigger.dev/openai": "1.0.0-next.0",
"@trigger.dev/database": "0.0.0",
"@trigger.dev/init": "0.2.0",
"@trigger.dev/integration-kit": "2.0.0-next.1",
"@trigger.dev/resend": "1.0.0-next.1"
},
"changesets": [
"bright-horses-bathe",
"chatty-cars-agree",
"chilly-meals-warn",
"clever-bats-learn",
"curly-hornets-sort",
"dirty-needles-kneel",
"eight-buckets-agree",
"eighty-queens-prove",
"four-ligers-perform",
"great-meals-listen",
"heavy-mails-explain",
"itchy-rockets-provide",
"metal-donkeys-relate",
"modern-emus-suffer",
"quick-stingrays-own",
"strong-cars-visit",
"stupid-files-cough",
"sweet-snails-refuse",
"tame-flowers-invite",
"tough-walls-share",
"wild-rats-mix"
]
}
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Fixed the help defaults
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Fixed an issue that cause environment variables to be incorrectly added to .env.local
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/nextjs": patch
---
Loosen the peer dep requirement on next
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/init": patch
---
Fixed CLI showing undefined when not specifying a trigger-url flag
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/resend": patch
---
Adding the resend integration package
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/nextjs": patch
---
Make sure nextjs app routes have the correct types
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/nextjs": patch
---
Update the devDependency on nextjs to be more permissive
+9 -1
View File
@@ -3,6 +3,7 @@
.github
# editor
.idea
.vscode
# dependencies
node_modules
.pnp
@@ -34,4 +35,11 @@ cypress/screenshots
cypress/videos
apps/**/styles/tailwind.css
packages/**/styles/tailwind.css
packages/**/styles/tailwind.css
.changeset
examples
CHANGESETS.md
CONTRIBUTING.md
README.md
LICENSE
+11 -14
View File
@@ -1,13 +1,7 @@
# YOU MUST SET THESE VARIABLES
# Resend is an email service used for signing in to Trigger.dev via a Magic Link.
### Visit https://resend.com, create an account and get your API key. Then insert it below along with your From and Reply To email addresses. Visit https://resend.com/docs for more information.
RESEND_API_KEY=<api_key>
FROM_EMAIL=
REPLY_TO_EMAIL=
# YOU MIGHT LIKE TO MODIFY THESE VARIABLES
SESSION_SECRET=abcdef1234
MAGIC_LINK_SECRET=abcdef1234
ENCRYPTION_KEY=ae13021afef0819c3a307ad487071c06 # Must be a random 16 byte hex string. You can generate an encryption key by running `openssl rand -hex 16` in your terminal
LOGIN_ORIGIN=http://localhost:3030
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres?schema=public
REMIX_APP_PORT=3030
@@ -16,17 +10,20 @@ APP_ORIGIN=http://localhost:3030
NODE_ENV=development
# OPTIONAL VARIABLES
AUTH_GITHUB_CLIENT_ID=
AUTH_GITHUB_CLIENT_SECRET=
# This is used for logging in via GitHub. You can leave these commented out if you don't want to use GitHub for authentication.
# AUTH_GITHUB_CLIENT_ID=
# AUTH_GITHUB_CLIENT_SECRET=
# Resend is an email service used for signing in to Trigger.dev via a Magic Link.
# Emails will print to the console if you leave these commented out
### Visit https://resend.com, create an account and get your API key. Then insert it below along with your From and Reply To email addresses. Visit https://resend.com/docs for more information.
# RESEND_API_KEY=<api_key>
# FROM_EMAIL=
# REPLY_TO_EMAIL=
# CLOUD VARIABLES
POSTHOG_PROJECT_KEY=
PLAIN_API_KEY=
SEED_CLOUD=
SEED_CLOUD_EMAIL=
SEED_CLOUD_GITHUB_ACCESS_TOKEN=
SEED_CLOUD_SLACK_ACCESS_TOKEN=
SEED_CLOUD_USER_GITHUB_ACCESS_TOKEN=
CLOUD_AIRTABLE_CLIENT_ID=
CLOUD_AIRTABLE_CLIENT_SECRET=
CLOUD_GITHUB_CLIENT_ID=
+22 -48
View File
@@ -5,7 +5,23 @@ on:
branches:
- main
tags:
- "v*"
- "v.docker.*"
paths:
- ".github/workflows/publish.yml"
- "packages/**"
- "!packages/**/*.md"
- "!packages/**/*.eslintrc"
- "apps/**"
- "!apps/**/*.md"
- "!apps/**/*.eslintrc"
- "integrations/**"
- "!integrations/**/*.md"
- "!integrations/**/*.eslintrc"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "turbo.json"
- "docker/Dockerfile"
- "docker/scripts/**"
permissions:
id-token: write
@@ -63,6 +79,10 @@ jobs:
run: |
IMAGE_TAG="${GITHUB_REF#refs/tags/}"
if [[ $GITHUB_REF == refs/tags/* ]]; then
if [[ $IMAGE_TAG == v.docker.* ]]; then
ORIGINAL_VERSION="${IMAGE_TAG#v.docker.}"
IMAGE_TAG="v${ORIGINAL_VERSION}"
fi
echo "IMAGE_TAG=${IMAGE_TAG}"
elif [[ $GITHUB_REF == refs/heads/* ]]; then
IMAGE_TAG="${GITHUB_REF#refs/heads/}"
@@ -71,8 +91,7 @@ jobs:
echo "Invalid reference: ${GITHUB_REF}"
exit 1
fi
echo ::set-output name=version::${IMAGE_TAG}
echo "::set-output name=version::${IMAGE_TAG}"
- name: 🔢 Get the commit hash
id: get_commit
run: |
@@ -106,48 +125,3 @@ jobs:
env:
REGISTRY: ghcr.io/triggerdotdev
REPOSITORY: trigger.dev
- name: 🌐 Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::067435167850:role/GitHubAction-AssumeRoleWithAction
aws-region: ${{ env.AWS_REGION }}
- name: 🌐 Login to ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v1
- name: 🌐 Push to Amazon ECR
run: |
docker tag release_build_image $REGISTRY/$REPOSITORY:$IMAGE_TAG
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
env:
REGISTRY: ${{ steps.ecr-login.outputs.registry }}
REPOSITORY: trigger.dev
IMAGE_TAG: ${{ steps.get_version.outputs.version }}
- name: 🌐 Push 'latest' to Amazon ECR
if: startsWith(github.ref, 'refs/tags/')
run: |
docker tag release_build_image $REGISTRY/$REPOSITORY:latest
docker push $REGISTRY/$REPOSITORY:latest
env:
REGISTRY: ${{ steps.ecr-login.outputs.registry }}
REPOSITORY: trigger.dev
- name: 🌐 Push commit hash to Amazon ECR
if: startsWith(github.ref, 'refs/heads/')
run: |
docker tag release_build_image $REGISTRY/$REPOSITORY:$IMAGE_TAG
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
env:
REGISTRY: ${{ steps.ecr-login.outputs.registry }}
REPOSITORY: trigger.dev
IMAGE_TAG: ${{ steps.get_version.outputs.version }}-${{ steps.get_commit.outputs.sha_short }}
- name: 🚀 Trigger Test Deploy
if: github.ref == 'refs/heads/main' && github.event_name == 'push' && github.repository == 'triggerdotdev/trigger.dev'
env:
DEPLOY_TEST_HOOK: ${{ secrets.DEPLOY_TEST_HOOK }}
run: |
curl -X POST $DEPLOY_TEST_HOOK
+10
View File
@@ -4,6 +4,16 @@ on:
push:
branches:
- main
paths:
- ".github/workflows/release.yml"
- "packages/**"
- "!packages/**/*.md"
- "changesets/**"
- "integrations/**"
- "!integrations/**/*.md"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "turbo.json"
jobs:
release:
+1 -1
View File
@@ -1 +1 @@
{"workspaceId":"63e5e42daf9a537ba8d9503c"}
{ "workspaceId": "63e5e42daf9a537ba8d9503c" }
+11
View File
@@ -0,0 +1,11 @@
node_modules
.env
.env.local
pnpm-lock.yaml
tailwind.css
.babelrc.json
**/.react-email/
**/storybook-static/
**/.changeset/
**/build/
**/dist/
+1 -8
View File
@@ -5,19 +5,12 @@
"version": "0.2.0",
"configurations": [
{
"command": "pnpm run idev --filter webapp",
"command": "pnpm run dev --filter webapp",
"name": "Run webapp",
"request": "launch",
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev --filter integrations",
"name": "Run integrations",
"request": "launch",
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"type": "chrome",
"request": "launch",
-19
View File
@@ -1,19 +0,0 @@
{
"folders": [
{
"name": "trigger.dev",
"path": "../"
},
{
"name": "Webapp",
"path": "../apps/webapp"
},
{
"name": "Integrations",
"path": "../apps/integrations"
}
],
"settings": {
"vitest.commandLine": "pnpm exec vitest"
}
}
+245
View File
@@ -0,0 +1,245 @@
# Contributing to Trigger.dev
Thank you for taking the time to contribute to Trigger.dev. Your involvement is not just welcomed, but we encourage it! 🚀
Please take some time to read this guide to understand contributing best practices for Trigger.dev.
Thank you for helping us make Trigger.dev even better! 🤩
## Developing
The development branch is `main`. This is the branch that all pull
requests should be made against. The changes on the `main`
branch are tagged into a release monthly.
### Prerequisites
- [Node.js](https://nodejs.org/en) version >=16.x
- [pnpm package manager](https://pnpm.io/installation) version 7
- [Docker](https://www.docker.com/get-started/)
### Setup
1. Clone the repo into a public GitHub repository or [fork the repo](https://github.com/triggerdotdev/trigger.dev/fork). If you plan to distribute the code, keep the source code public to comply with the [Apache Licence 2.0](https://github.com/triggerdotdev/trigger.dev/blob/main/LICENSE).
```
git clone https://github.com/triggerdotdev/trigger.dev.git
```
> If you are on windows, run the following command on gitbash with admin privileges:
> `git clone -c core.symlinks=true https://triggerdotdev/trigger.dev.git`
2. Navigate to the project folder
```
cd trigger.dev
```
3. Install the required packages using pnpm.
```
pnpm i
```
4. Create your `.env` files
```
cp .env.example .env && cp packages/database/.env.example packages/database/.env
```
5. Open the root `.env` file and generate a new value for `ENCRYPTION_KEY`:
`ENCRYPTION_KEY` is used to two-way encrypt OAuth access tokens and so you'll probably want to actually generate a unique value, and it must be a random 16 byte hex string. You can generate one with the following command:
```sh
openssl rand -hex 16
```
Feel free to update `SESSION_SECRET` and `MAGIC_LINK_SECRET` as well using the same method.
6. Start Docker. This starts the required services like Postgres. If this is your first time using Docker, consider going through this [guide](DOCKER_INSTALLATION.md)
```
pnpm run docker
```
7. Migrate the database
```
pnpm run db:migrate
```
8. Build the app
```
pnpm run build --filter webapp
```
9. Run the seed script
```
pnpm run db:seed
```
10. Run the app. See the section below.
## Running
1. You can run the app with:
```
pnpm run dev --filter webapp
```
It should run on port `3030`: [http://localhost:3030](http://localhost:3030/)
2. Once the app is running click the magic link button and enter your email.
3. Check your terminal, the magic link email should have printed out as following:
```sh
webapp:dev: Log in to Trigger.dev
webapp:dev:
webapp:dev: Click here to log in with this magic link
webapp:dev: [http://localhost:3030/magic?token=U2FsdGVkX18OvB0JxgaswTLCSbaRz%2FY82TN0EZWhSzFyZYwgG%2BIzKVTkeiaOtWfotPw7F8RwFzCHh53aBpMEu%2B%2B%2FItb%2FcJYh89MSjc3Pz92bevoEjqxSQ%2Ff%2BZbks09JOpqlBbYC3FzGWC8vuSVFBlxqLXxteSDLthZSUaC%2BS2LaA%2BJgp%2BLO7hgjAaC2lXbCHrM7MTgTdXOFt7i0Dvvuwz6%2BWY25RnfomZOPqDsyH0xz8Q2rzPTz0Xu53WSXrZ1hd]
webapp:dev:
webapp:dev: If you didn't try to log in, you can safely ignore this email.
```
Paste the magic link shown in your terminal into your browser to login.
## Adding and running migrations
1. Modify packages/database/prisma/schema.prisma file
2. Change directory to the packages/database folder
```sh
cd packages/database
```
3. Generate the Prisma client
```sh
pnpm run generate
```
The above updates the prisma client generated into node_modules/.prisma/client folder. This helps with typing of relevant prisma models. It ensures typescript
recognizes fields added or removed from a model and type-checks appropriately.
4. Create and apply the migrations
```
pnpm run db:migrate:dev
```
This creates a migration file and executes the migrations against your database and applies changes to the database schema(s)
5. Commit generated migrations as well as changes to the schema.prisma file
6. If you're using VSCode you may need to restart the Typescript server in the webapp to get updated type inference. Open a TypeScript file, then open the Command Palette (View > Command Palette) and run `TypeScript: Restart TS server`.
## Testing CLI changes
To test CLI changes, follow the steps below:
1. Build the CLI and watch for changes
```sh
cd packages/cli
pnpm run dev
```
2. Open a new Terminal window and run the webapp locally and then create a new project in the dashboard. Copy out the dev API key.
3. Create a new temporary Next.js app in examples directory
```sh
pnpm create next-app@latest
```
Follow the prompts to create a TypeScript project using the App Directory.
4. Then once that's finished, add the `@trigger.dev/cli` to the `devDependencies` of the newly created Next.js app's `package.json` file, like so:
```json
{
"devDependencies": { "@trigger.dev/cli": "workspace:*" }
}
```
5. Open a new terminal window, navigate into the example, and initialize the CLI:
```sh
cd examples/your-newly-created-nextjs-project
pnpm i
pnpm exec trigger-cli init
```
6. When prompted, select `self-hosted` and enter `localhost:3030` for your local version of the webapp. When asked for an API key, use the key you copied earlier.
7. Run the CLI
```sh
pnpm exec trigger-cli dev
```
8. After running the CLI, start your newly created Next.js project. You should now be able to see the changes.
9. Please remember to delete the temporary project you created after you've tested the changes, and before you raise a PR.
## Add sample jobs
The [examples/jobs-starter](./examples/jobs-starter/) project defines simple jobs you can get started with.
1. `cd` into `examples/jobs-starter`
2. Create a `.env.local` file with the following content,
replacing `[TRIGGER_DEV_API_KEY]` with an actual key:
```env
TRIGGER_API_KEY=[TRIGGER_DEV_API_KEY]
TRIGGER_API_URL=http://localhost:3030
```
`TRIGGER_API_URL` is used to configure the URL for your Trigger.dev instance,
where the jobs will be registered.
3. Run the `jobs-starter` app:
```sh
pnpm dev
```
4. Navigate to your trigger.dev instance ([http://localhost:3030](http://localhost:3030/)), to see the jobs.
You can use the test feature to trigger them.
## Making a pull request
**If you get errors, be sure to fix them before committing.**
- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating you PR.
- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. See more about [Linking a pull request to an issue
](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
- Be sure to fill the PR Template accordingly.
## Adding changesets
We use [changesets](https://github.com/changesets/changesets) to manage our package versions and changelogs. If you've never used changesets before, first read [their guide here](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).
If you are contributing a change to any packages in this monorepo (anything in either the `/packages` or `/integrations` directories), then you will need to add a changeset to your Pull Requests before they can be merged.
To add a changeset, run the following command in the root of the repo
```sh
pnpm run changeset:add
```
Here's an example of creating a `patch` changeset for the `@trigger.dev/github` and `@trigger.dev/slack` packages (click to view):
[![asciicast](https://asciinema.org/a/599228.svg)](https://asciinema.org/a/599228)
You will be prompted to select which packages to include in the changeset. Only select the packages that you have made changes for.
Most of the time the changes you'll make are likely to be categorized as patch releases. If you feel like there is the need for a minor or major release of the package based on the changes being made, add the changeset as such and it will be discussed during PR review.
## Troubleshooting
### EADDRINUSE: address already in use :::3030
When receiving the following error message:
```sh
webapp:dev: Error: listen EADDRINUSE: address already in use :::3030
```
The process running on port `3030` should be destroyed.
1. Get the `PID` of the process running on PORT `3030`
```sh
lsof -i :3030
```
2. Kill the process
```sh
sudo kill -9 <PID>
```
-32
View File
@@ -1,32 +0,0 @@
# Trigger.dev Deployment Guide
## StreamNative Cloud - Hosted Pulsar
### Generic client credentials
```sh
snctl auth export-service-account webapp --key-file webapp-credentials.json
snctl auth export-service-account websocketserver --key-file wss-credentials.json
```
### Topics
#### Triggers (`persistent://triggerdotdev/workflows/triggers`)
Events that trigger workflows to run. These are sent by the "platform" and read by the Web Socket Servers, which then coordinate with the hosts for running the workflows
#### Run Commands (`persistent://triggerdotdev/workflows/run-commands`)
These are events that come from hosts and are published by the Web Socket Servers, e.g. Sending Integration Requests, Sending Logs, Initializing a Delay
#### Run Command Responses (`persistent://triggerdotdev/workflows/run-command-responses`)
These are events that come from the platform and are read by the Web Socket Servers, to resolve or reject a previous Run Command
#### Integration Requests (`persistent://triggerdotdev/queues/integration-requests`)
This is an internal queue used by the platform to perform integration requests, and retry them.
#### App Task Queue (`persistent://triggerdotdev/queues/background-tasks`)
This is an internal queue used by the platform to do tasks in a queue. Basically a background job system.
-231
View File
@@ -1,231 +0,0 @@
# Initial setup
## Prerequisites
### Pulsar requirements
1. Ensure you have Homebrew installed by running `which brew` in terminal. If it's not found then you should install it: https://brew.sh/. Run `which brew` again to check it's found. If it's not you may need to [add it your path](https://stackoverflow.com/questions/36657321/after-installing-homebrew-i-get-zsh-command-not-found-brew)
2. Run `brew install libpulsar` to install the C++ libraries that the pulsar-client depends on
3. Make sure you have Python installed on your machine by running `which python3` in terminal.
4. If python isn't found then you should install it: https://www.python.org/downloads/. In a new terminal window run `which python3` again.
5. Run `npm config set python /the/path/from/the/which/python3/command` inserting the path from step 2 or 3
6. Install node-gyp: `npm install -g node-gyp`
7. Make sure you have the Xcode command line tools installed by running `xcode-select --install` from the terminal. If it says they're already installed then you're set.
8. Run this in the terminal:
```sh
export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:$(brew --prefix)/include"
export LIBRARY_PATH="$LIBRARY_PATH:$(brew --prefix)/lib"
export PULSAR_CPP_DIR=/opt/homebrew/Cellar/libpulsar/3.1.0
```
9. Run `pnpm install` in the same terminal window.
## Building and running the webapp
> **Warning**
> All the following commands should be launched from the **monorepo root directory**
1. Install the dependencies.
```bash
pnpm install
```
2. Optionally, if you are testing auth (pizzly) or webhooks (webapp) then you'll need to use ngrok to proxy internet traffic to your local machine
Get access to ngrok and then follow the instructions here to get it setup: https://ngrok.com/download (use homebrew) and make sure to authenticate.
Then run the following scripts to start proxying:
```sh
./scripts/proxy-pizzly.sh dan-pizzly-dev
./scripts/proxy-webapp.sh dan-trigger-dev
```
3. Environment variables. You will need to create copies of the `.env.example` files in `app/webapp`
```sh
cp ./apps/webapp/.env.example ./apps/webapp/.env
```
Then you will need to fill in the fields with real values.
You also need to create the `pizzly-server.env` files under the `.docker` directory:
```sh
cp ./.docker/pizzly-server.env.example ./.docker/pizzly-server.env
```
Next, update the `AUTH_CALLBACK_URL` env var in the `pizzly-server.env` env file with the value provided to the `./scripts/proxy-pizzly.sh` command. Using the example above the `AUTH_CALLBACK_URL` would be `AUTH_CALLBACK_URL=https://dan-pizzly-dev.eu.ngrok.io/oauth/callback`.
If you aren't proxying pizzly according to step 2, then leave the `pizzly-server.env` file empty.
If you are proxying the webapp according to step 2 then in `webapp/.env`, set the `APP_ORIGIN` to the `NGROK_SUBDOMAIN` provided to the `./scripts/proxy-webapp.sh` command, e.g. `APP_ORIGIN=https://dan-trigger-dev.eu.ngrok.io`
4. Start postgresql, redis, pizzly server, followed by pulsar standalone
```bash
pnpm run docker:services
pnpm run docker:pulsar
```
> **Note:** The npm script will complete while Docker sets up the container in the background. Ensure that Docker has finished and your container is running before proceeding.
5. Generate prisma schema
```bash
pnpm run generate
```
6. Run the Prisma migration to the database
```bash
pnpm run db:migrate:deploy
```
7. Run the first build (with dependencies via the `...` option)
```bash
pnpm run build --filter=webapp...
```
**Running simply `pnpm run build` will build everything, including the Remix app.**
8. Run the Remix dev server
```bash
pnpm run dev --filter=webapp
```
## Attaching an API integration using Pizzly
```bash
PIZZLY_HOSTPORT=http://localhost:3004 npx pizzly config:create github github <client-id> <client-secret> "repo,user"
```
## Tests, Typechecks, Lint, Install packages...
Check the `turbo.json` file to see the available pipelines.
- Run the Cypress tests and Dev
```bash
pnpm run test:e2e:dev --filter=webapp
```
- Lint everything
```bash
pnpm run lint
```
- Typecheck the whole monorepo
```bash
pnpm run typecheck
```
- Test the whole monorepo
```bash
pnpm run test
or
pnpm run test:dev
```
- How to install an npm package in the Remix app ?
```bash
pnpm add dayjs --filter webapp
```
- Tweak the tsconfigs, eslint configs in the `config-package` folder. Any package or app will then extend from these configs.
# Running a workflow locally
## After pulling a change
1. Ensure there are no database migrations to run
```bash
pnpm run db:migrate:dev
```
2. Generate the Prisma database client
```bash
pnpm run generate
```
3. Install packages
```bash
pnpm install
```
4. Build everything
```bash
pnpm run build
```
5. Install packages again, this makes sure the local packages are linked
```bash
pnpm install
```
## Running the servers
1. Ensure the docker containers are running
```bash
pnpm run docker:services
pnpm run docker:pulsar
```
2. Run the webapp
```bash
pnpm run dev --filter=webapp
```
3. Run the Web Socket Server
```bash
pnpm run dev --filter=wss
```
4. Build all the @trigger.dev/\* packages
```bash
pnpm run dev --filter="@trigger.dev/*"
```
## Running the smoke test
1. Run the smoke test workflow
```bash
cd ./examples/smoke-test
pnpm run dev
```
2. Running the workflow requires you to send data to the local API.
You can use this cURL command to send a `user.created` event. This will run the workflow and generate the corresponding logs.
```bash
curl --request POST \
--url http://localhost:3000/api/v1/events \
--header 'Authorization: Bearer trigger_dev_zC25mKNn6c0q' \
--header 'Content-Type: application/json' \
--data '{
"name": "user.created",
"payload": {
"id": "123"
}
}'
```
## Dependency & Package graph
![Dependency Graph](assets/dependencyGraph.png)
## Warp Launch Configuration
Setup a custom launch configuration for the Warp terminal ([docs here](https://docs.warp.dev/features/sessions/launch-configurations)) by copying the `.warp/triggerdotdev.yaml.example` file to `~/.warp/launch_configurations/triggerdotdev.yaml`. Make sure you edit the file and replace `<your-trigger-dev>` and `<your-pizzly-dev>` with your custom ngrok subdomains.
+126
View File
@@ -0,0 +1,126 @@
This guide covers installing Docker and Docker Compose. If you're looking for instructions for running Trigger.dev in docker, [see here](https://github.com/triggerdotdev/docker).
## Setting up Docker for the first time.
In the contributing guide of Trigger.dev, there's a section that requires you to start Docker.
If you don't have Docker installed on your machine, you'll run into some complications (errors).
Below are the steps on how you can avoid that.
First you need to setup docker-compose as it is an underlying tool that this command: `pnpm run docker` fires behind the scene.
## Linux
To install Docker Compose on Linux Ubuntu via the terminal, you can follow these steps:
1. Update the package index on your system by running the following command:
```shell
sudo apt update
```
2. Install the required dependencies by running the following command:
```shell
sudo apt install curl
```
3. Download the Docker Compose binary into the `/usr/local/bin` directory using the `curl` command:
```shell
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
```
4. Set the appropriate permissions to make the `docker-compose` binary executable:
```shell
sudo chmod +x /usr/local/bin/docker-compose
```
5. Verify that Docker Compose has been successfully installed by running the following command:
```shell
docker-compose --version
```
This command should display the version information of Docker Compose without any errors.
After following these steps, you should have Docker Compose installed on your Ubuntu system, and you can use it by running `docker-compose` commands in the terminal.
When you've verified that the `docker-compose` package is installed and you proceed to start Docker with `pnpm run docker`.
You'll probably get an error similar to the one below:
```shell
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
ELIFECYCLE Command failed with exit code 1.
```
The error message suggests that the Docker daemon is not running on your system. The Docker daemon is responsible for managing and running Docker containers.
To resolve this issue, you may need to install Docker properly on your Ubuntu system. Here are the steps to install Docker on Ubuntu:
1. Update the package index on your system by running the following command:
```shell
sudo apt update
```
2. Install the necessary packages to allow apt to use repositories over HTTPS:
```shell
sudo apt install apt-transport-https ca-certificates curl software-properties-common
```
3. Add the official Docker GPG key to your system by running the following command:
```shell
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
4. Add the Docker repository to the APT sources list:
```shell
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
5. Update the package index again:
```shell
sudo apt update
```
6. Install Docker by running the following command:
```shell
sudo apt install docker-ce docker-ce-cli containerd.io
```
7. After the installation is complete, verify that Docker is installed correctly by running the following command:
```shell
docker --version
```
This command should display the version information of Docker without any errors.
Once Docker is installed and verified, you should be able to start the Docker daemon and run the `pnpm run docker` command without encountering any issues.
## Windows
1. Download the Docker Desktop installer from the Docker website: [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop)
2. Run the installer and follow the instructions to install Docker Desktop.
3. After installation, Docker Desktop should be running automatically.
## macOS
1. Download the Docker Desktop installer from the Docker website: [Docker Desktop for Mac](https://www.docker.com/products/docker-desktop)
2. Run the installer and follow the instructions to install Docker Desktop.
3. After installation, Docker Desktop should be running automatically.
Please note that the instructions provided above are for the most common scenarios. For specific versions or different distributions, it's always a good idea to consult the official Docker documentation for the respective operating systems.
+4 -44
View File
@@ -7,13 +7,15 @@
### The background jobs framework for Next.js
[Discord](https://discord.gg/bpVDSjcG) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
[Discord](https://discord.gg/JtBAxBr2m3) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/triggerdotdev.svg?style=social&label=Follow%20%40trigger.dev)](https://twitter.com/triggerdotdev)
[![GitHub Repo stars](https://img.shields.io/github/stars/triggerdotdev/trigger.dev?style=social)](https://github.com/triggerdotdev/trigger.dev)
</div>
# About Trigger.dev
Create long-running jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays.
### Long running Jobs on serverless
@@ -60,46 +62,4 @@ We provide an official trigger.dev docker image you can use to easily self-host
## Development
### Prerequisites
- [Node.js](https://nodejs.org/en) version >=18.x
- [pnpm package manager](https://pnpm.io/installation) version 7
- [Docker](https://www.docker.com/get-started/)
### Setup
1. Clone the repo into a public GitHub repository or [fork the repo](https://github.com/triggerdotdev/trigger.dev/fork). If you plan to distribute the code, keep the source code public to comply with the [Apache Licence 2.0](https://github.com/triggerdotdev/trigger.dev/blob/main/LICENSE).
```
git clone https://github.com/triggerdotdev/trigger.dev.git
```
> If you are on windows, run the following command on gitbash with admin privileges:
> `git clone -c core.symlinks=true https://triggerdotdev/trigger.dev.git`
2. Navigate to the project folder
```
cd trigger.dev
```
3. Install the required packages using pnpm.
```
pnpm i
```
4. Create your `.env` files
```
cp .env.example .env && cp packages/database/.env.example packages/database/.env
```
Open the root `.env` file and fill in the required values.
6. Start Docker. This starts the required services like Postgres.
```
pnpm run docker
```
7. Migrate the database
```
pnpm run db:migrate
```
8. Run the seed script
```
pnpm run db:seed
```
5. Run the project. It should run on `port:3030`
```
pnpm run dev --filter webapp
```
To setup and develop locally or contribute to the open source project, follow our [developement guide](./CONTRIBUTING.md).
+1 -5
View File
@@ -1,9 +1,5 @@
{
"extends": [
"@remix-run/eslint-config",
"@remix-run/eslint-config/node",
"prettier"
],
"extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "prettier"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": [
"error",
+3 -1
View File
@@ -15,4 +15,6 @@ node_modules
build-storybook.log
.out
.storybook-out
storybook-static
storybook-static
/prisma/seed.js
+1 -4
View File
@@ -20,10 +20,7 @@ const config: StorybookConfig = {
},
};
},
stories: [
"../app/**/stories/*.mdx",
"../app/**/stories/*.stories.@(js|jsx|ts|tsx)",
],
stories: ["../app/**/stories/*.mdx", "../app/**/stories/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
+1 -3
View File
@@ -35,9 +35,7 @@ const preview: Preview = {
return (
<OperatingSystemContextProvider platform="mac">
<LocaleContextProvider
locales={window.navigator.languages as string[]}
>
<LocaleContextProvider locales={window.navigator.languages as string[]}>
<RemixStub initialEntries={["/"]} />
</LocaleContextProvider>
</OperatingSystemContextProvider>
@@ -1,11 +1,6 @@
export function DynamicTriggerIcon({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 21 21"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<svg viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
<path
fillRule="evenodd"
clipRule="evenodd"
@@ -0,0 +1,13 @@
export function ErrorIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="7" fill="white" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18 10C18 14.4183 14.4183 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2C14.4183 2 18 5.58172 18 10ZM10 5C10.4142 5 10.75 5.33579 10.75 5.75V10.25C10.75 10.6642 10.4142 11 10 11C9.58579 11 9.25 10.6642 9.25 10.25V5.75C9.25 5.33579 9.58579 5 10 5ZM10 15C10.5523 15 11 14.5523 11 14C11 13.4477 10.5523 13 10 13C9.44771 13 9 13.4477 9 14C9 14.5523 9.44771 15 10 15Z"
fill="currentColor"
/>
</svg>
);
}
+1 -6
View File
@@ -1,11 +1,6 @@
export function WebhookIcon({ className }: { className?: string }) {
return (
<svg
className={className}
viewBox="0 0 190 180"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<svg className={className} viewBox="0 0 190 180" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill="currentColor"
fillRule="evenodd"
+5 -8
View File
@@ -1,7 +1,7 @@
import { ReactNode } from "react";
import { MainCenteredContainer } from "./layout/AppLayout";
import { Header1, Header2 } from "./primitives/Headers";
import { NamedIcon, NamedIconInBox } from "./primitives/NamedIcon";
import { Header2 } from "./primitives/Headers";
import { NamedIconInBox } from "./primitives/NamedIcon";
import { Paragraph } from "./primitives/Paragraph";
type ComingSoonProps = {
@@ -16,16 +16,13 @@ export function ComingSoon({ title, description, icon }: ComingSoonProps) {
<div className="flex flex-col justify-center rounded border border-slate-800 bg-slate-850">
<div className="flex items-center gap-2 border-b border-slate-750 px-4 py-4">
{typeof icon === "string" ? (
<NamedIconInBox
name={icon}
className={"h-10 w-10 bg-midnight-800"}
/>
<NamedIconInBox name={icon} className={"h-10 w-10 bg-midnight-800"} />
) : (
icon
)}
<div className="mt-0.5 flex flex-col">
<div className="mt-0.5 flex flex-col gap-y-1">
<Paragraph variant="extra-extra-small/caps">Coming soon</Paragraph>
<Header1 className="-mt-0.5">{title}</Header1>
<Header2 className="-mt-0.5">{title}</Header2>
</div>
</div>
<Paragraph variant="small" className="p-4">
@@ -0,0 +1,50 @@
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
import { LinkButton } from "./primitives/Buttons";
import { Header1, Header3 } from "./primitives/Headers";
import { friendlyErrorDisplay } from "~/utils/httpErrors";
type ErrorDisplayOptions = {
button?: {
title: string;
to: string;
};
};
export function RouteErrorDisplay(options?: ErrorDisplayOptions) {
const error = useRouteError();
return (
<>
{isRouteErrorResponse(error) ? (
<ErrorDisplay
title={friendlyErrorDisplay(error.status, error.statusText).title}
message={
error.data.message ?? friendlyErrorDisplay(error.status, error.statusText).message
}
{...options}
/>
) : error instanceof Error ? (
<ErrorDisplay title={error.name} message={error.message} {...options} />
) : (
<ErrorDisplay title="Oops" message={JSON.stringify(error)} {...options} />
)}
</>
);
}
type DisplayOptionsProps = {
title: string;
message?: string;
} & ErrorDisplayOptions;
export function ErrorDisplay({ title, message, button }: DisplayOptionsProps) {
return (
<div className="p-4">
<Header1 className="mb-4 border-b border-slate-800 pb-4">{title}</Header1>
{message && <Header3>{message}</Header3>}
<LinkButton to={button ? button.to : "/"} variant="primary/medium" className="mt-8">
{button ? button.title : "Home"}
</LinkButton>
</div>
);
}
+18 -37
View File
@@ -1,14 +1,8 @@
import { conform, useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { ChatBubbleLeftRightIcon } from "@heroicons/react/24/solid";
import {
Form,
useActionData,
useLocation,
useNavigation,
} from "@remix-run/react";
import { useState } from "react";
import { feedbackTypeLabel, schema } from "~/routes/resources.feedback";
import { Form, useActionData, useLocation, useNavigation } from "@remix-run/react";
import { ReactNode, useState } from "react";
import { FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback";
import { Button } from "./primitives/Buttons";
import { Fieldset } from "./primitives/Fieldset";
import { FormButtons } from "./primitives/FormButtons";
@@ -24,16 +18,15 @@ import {
SelectTrigger,
SelectValue,
} from "./primitives/Select";
import {
Sheet,
SheetBody,
SheetContent,
SheetHeader,
SheetTrigger,
} from "./primitives/Sheet";
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "./primitives/Sheet";
import { TextArea } from "./primitives/TextArea";
export function Feedback() {
type FeedbackProps = {
button: ReactNode;
defaultValue?: FeedbackType;
};
export function Feedback({ button, defaultValue = "bug" }: FeedbackProps) {
const [open, setOpen] = useState(false);
const location = useLocation();
const lastSubmission = useActionData();
@@ -58,31 +51,21 @@ export function Feedback() {
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild={true}>
<Button
variant="secondary/small"
LeadingIcon={ChatBubbleLeftRightIcon}
shortcut={{ key: "f" }}
>
Send us feedback
</Button>
</SheetTrigger>
<SheetTrigger asChild={true}>{button}</SheetTrigger>
<SheetContent size="sm">
<SheetHeader className="justify-between">Give us feedback</SheetHeader>
<SheetHeader className="justify-between">Help & feedback</SheetHeader>
<SheetBody>
<Paragraph variant="small" className="mb-4">
We'd love to hear your feedback, good, bad or ugly.
Use this form to ask for help or give us feedback. We read every message and will get
back to you as soon as we can.
</Paragraph>
<Form method="post" action="/resources/feedback" {...form.props}>
<Fieldset>
<input
value={location.pathname}
{...conform.input(path, { type: "hidden" })}
/>
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
<InputGroup>
<Label>What kind of feedback do you have?</Label>
<Label>How can we help?</Label>
<SelectGroup>
<Select {...conform.input(feedbackType)} defaultValue={"bug"}>
<Select {...conform.input(feedbackType)} defaultValue={defaultValue}>
<SelectTrigger size="medium" width="full">
<SelectValue placeholder="Type" />
</SelectTrigger>
@@ -95,9 +78,7 @@ export function Feedback() {
</SelectContent>
</Select>
</SelectGroup>
<FormError id={feedbackType.errorId}>
{feedbackType.error}
</FormError>
<FormError id={feedbackType.errorId}>{feedbackType.error}</FormError>
</InputGroup>
<InputGroup>
<Label>Message</Label>
@@ -0,0 +1,14 @@
import { H, HighlightOptions } from "highlight.run";
import { useEffect } from "react";
interface Props extends HighlightOptions {
projectId?: string;
}
export function HighlightInit({ projectId, ...highlightOptions }: Props) {
useEffect(() => {
projectId && H.init(projectId, highlightOptions);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return null;
}
@@ -2,17 +2,12 @@ import { Form } from "@remix-run/react";
import { Paragraph } from "./primitives/Paragraph";
import { Button } from "./primitives/Buttons";
export function ImpersonationBanner({
impersonationId,
}: {
impersonationId: string;
}) {
export function ImpersonationBanner({ impersonationId }: { impersonationId: string }) {
return (
<div className="grid h-8 grid-cols-3 items-center bg-gradient-to-r from-amber-500 via-amber-400 to-amber-500 px-4 text-center">
<span></span>
<Paragraph variant="small">
You are impersonating{" "}
<span className="font-semibold">{impersonationId}</span>
You are impersonating <span className="font-semibold">{impersonationId}</span>
</Paragraph>
<Form
action="/resources/impersonation"
@@ -10,9 +10,7 @@ export function NoMobileOverlay() {
<div className="absolute mx-8 flex flex-col items-center gap-6 rounded-lg bg-slate-850 px-8 py-10 text-center">
<XMarkIcon className="absolute top-14 h-8 w-8 text-rose-600" />
<DevicePhoneMobileIcon className="h-16 w-16 text-slate-500" />
<Paragraph>
Trigger.dev is currently only available on desktop.
</Paragraph>
<Paragraph>Trigger.dev is currently only available on desktop.</Paragraph>
<LinkButton to="https://trigger.dev" variant="primary/medium">
Back Home
</LinkButton>
@@ -4,13 +4,7 @@ import { cn } from "~/utils/cn";
export function UserProfilePhoto({ className }: { className?: string }) {
const user = useOptionalUser();
return (
<UserAvatar
avatarUrl={user?.avatarUrl}
name={user?.name}
className={className}
/>
);
return <UserAvatar avatarUrl={user?.avatarUrl} name={user?.name} className={className} />;
}
export function UserAvatar({
@@ -23,9 +17,9 @@ export function UserAvatar({
className?: string;
}) {
return avatarUrl ? (
<div>
<div className={cn("grid aspect-square place-items-center", className)}>
<img
className={cn("aspect-square rounded-full p-[7%]", className)}
className={cn("aspect-square rounded-full p-[7%]")}
src={avatarUrl}
alt={name ?? "User"}
/>
+8 -33
View File
@@ -2,16 +2,8 @@ import type { Language, PrismTheme } from "prism-react-renderer";
import Highlight, { defaultProps } from "prism-react-renderer";
import { forwardRef, useCallback, useState } from "react";
import { cn } from "~/utils/cn";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../primitives/Tooltip";
import {
ClipboardDocumentCheckIcon,
ClipboardIcon,
} from "@heroicons/react/24/solid";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
import { ClipboardDocumentCheckIcon, ClipboardIcon } from "@heroicons/react/24/solid";
import { Clipboard, ClipboardCheck, ClipboardCheckIcon } from "lucide-react";
//This is a fork of https://github.com/mantinedev/mantine/blob/master/src/mantine-prism/src/Prism/Prism.tsx
@@ -193,9 +185,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
const maxLineWidth = lineCount.toString().length;
let maxHeight: string | undefined = undefined;
if (maxLines && lineCount > maxLines) {
maxHeight = `calc(${
(maxLines + extraLinesWhenClipping) * 0.75 * 1.625
}rem + 1.5rem )`;
maxHeight = `calc(${(maxLines + extraLinesWhenClipping) * 0.75 * 1.625}rem + 1.5rem )`;
}
const highlightLines = highlightedRanges?.flatMap(([start, end]) =>
@@ -204,10 +194,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
return (
<div
className={cn(
"relative overflow-hidden rounded-md border border-slate-800",
className
)}
className={cn("relative overflow-hidden rounded-md border border-slate-800", className)}
style={{
backgroundColor: theme.plain.backgroundColor,
}}
@@ -226,9 +213,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
className={cn(
"absolute right-3 z-50 transition-colors duration-100 hover:cursor-pointer",
showChrome ? "top-10" : "top-3",
copied
? "text-emerald-500"
: "text-slate-500 hover:text-slate-300"
copied ? "text-emerald-500" : "text-slate-500 hover:text-slate-300"
)}
>
{copied ? (
@@ -244,12 +229,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
</TooltipProvider>
)}
<Highlight
{...defaultProps}
theme={theme}
code={code}
language={language}
>
<Highlight {...defaultProps} theme={theme} code={code} language={language}>
{({
className: inheritedClassName,
style: inheritedStyle,
@@ -285,15 +265,10 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
const lineNumber = index + 1;
const lineProps = getLineProps({ line, key: index });
let hasAnyHighlights = highlightLines
? highlightLines.length > 0
: false;
let hasAnyHighlights = highlightLines ? highlightLines.length > 0 : false;
let shouldDim = hasAnyHighlights;
if (
hasAnyHighlights &&
highlightLines?.includes(lineNumber)
) {
if (hasAnyHighlights && highlightLines?.includes(lineNumber)) {
shouldDim = false;
}
@@ -1,9 +1,6 @@
import { json as jsonLang } from "@codemirror/lang-json";
import type { ViewUpdate } from "@codemirror/view";
import type {
ReactCodeMirrorProps,
UseCodeMirror,
} from "@uiw/react-codemirror";
import type { ReactCodeMirrorProps, UseCodeMirror } from "@uiw/react-codemirror";
import { useCodeMirror } from "@uiw/react-codemirror";
import { useRef, useEffect } from "react";
import { getEditorSetup } from "./codeMirrorSetup";
@@ -30,10 +30,7 @@ export function getViewerSetup(): Array<Extension> {
return [drawSelection(), dropCursor(), bracketMatching(), lineNumbers()];
}
export function getEditorSetup(
showLineNumbers = true,
showHighlights = true
): Array<Extension> {
export function getEditorSetup(showLineNumbers = true, showHighlights = true): Array<Extension> {
const options = [
drawSelection(),
dropCursor(),
@@ -36,8 +36,9 @@ export function darkTheme(): Extension {
},
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{ backgroundColor: selection },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
backgroundColor: selection,
},
".cm-panels": { backgroundColor: darkBackground, color: ivory },
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
@@ -101,13 +102,7 @@ export function darkTheme(): Extension {
const jsonHeroHighlightStyle = HighlightStyle.define([
{ tag: tags.keyword, color: violet },
{
tag: [
tags.name,
tags.deleted,
tags.character,
tags.propertyName,
tags.macroName,
],
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
color: coral,
},
{ tag: [tags.function(tags.variableName), tags.labelName], color: malibu },
@@ -194,8 +189,9 @@ export function lightTheme(): Extension[] {
},
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{ backgroundColor: selection },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
backgroundColor: selection,
},
".cm-panels": { backgroundColor: darkBackground, color: ivory },
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
@@ -259,13 +255,7 @@ export function lightTheme(): Extension[] {
const jsonHeroHighlightStyle = tagHighlighter([
{ tag: tags.keyword, class: violet },
{
tag: [
tags.name,
tags.deleted,
tags.character,
tags.propertyName,
tags.macroName,
],
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
class: variableColor,
},
{
@@ -1,97 +1,41 @@
import { Paragraph, TextLink } from "~/components/primitives/Paragraph";
import { Link } from "@remix-run/react";
import { Paragraph } from "~/components/primitives/Paragraph";
import { StepNumber } from "~/components/primitives/StepNumber";
import { IntegrationIcon } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route";
import { Callout } from "../primitives/Callout";
import integrationButton from "./integration-button.png";
import ngrok from "./ngrok.png";
import publicUrl from "./public-url.png";
import { useAppOrigin } from "~/hooks/useAppOrigin";
import { useDevEnvironment } from "~/hooks/useEnvironments";
import { ClipboardField } from "../primitives/ClipboardField";
import { Button } from "../primitives/Buttons";
import { useJob } from "~/hooks/useJob";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { IntegrationIcon } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route";
import { jobTestPath } from "~/utils/pathBuilder";
import { CodeBlock } from "../code/CodeBlock";
import { InlineCode } from "../code/InlineCode";
import { EnvironmentLabel } from "../environments/EnvironmentLabel";
import { HelpPanelProps } from "../integrations/ApiKeyHelp";
import { HelpInstall } from "../integrations/HelpInstall";
import { HelpSamples } from "../integrations/HelpSamples";
import { Button, LinkButton } from "../primitives/Buttons";
import { Callout, variantClasses } from "../primitives/Callout";
import {
ClientTabs,
ClientTabsContent,
ClientTabsList,
ClientTabsTrigger,
} from "../primitives/ClientTabs";
import { ClipboardField } from "../primitives/ClipboardField";
import { Header2 } from "../primitives/Headers";
import { TextLink } from "../primitives/TextLink";
import integrationButton from "./integration-button.png";
import selectEnvironment from "./select-environment.png";
import selectExample from "./select-example.png";
export function HowToSetupYourProject() {
const devEnvironment = useDevEnvironment();
const appOrigin = useAppOrigin();
return (
<>
<StepNumber stepNumber="1" title="Run your Next.js app" />
<StepNumber stepNumber="1" title="Run the CLI 'init' command in a Next.js project" />
<StepContentContainer>
<Paragraph>
Ensure your Next.js app is running locally on port 3000.
</Paragraph>
</StepContentContainer>
<StepNumber stepNumber="2" title="Create a tunnel to your Next.js app" />
<StepContentContainer>
<Paragraph spacing>
There are a few ways to do this, but we recommend using{" "}
<TextLink
href="https://trigger.dev/docs/documentation/guides/tunneling-localhost"
target="_blank"
>
ngrok
</TextLink>
. Its free and easy to use and required to create a tunnel, making
your local machine accessible to the internet.
</Paragraph>
<StepNumber stepNumber="a" title="Install ngrok" className="mb-2" />
<StepContentContainer>
<ClientTabs defaultValue="mac">
<ClientTabsList>
<ClientTabsTrigger value={"mac"}>Mac</ClientTabsTrigger>
<ClientTabsTrigger value={"windows"}>Windows</ClientTabsTrigger>
</ClientTabsList>
<ClientTabsContent value={"mac"}>
<ClipboardField
variant="primary/medium"
fullWidth={false}
value={`brew install ngrok/ngrok/ngrok`}
/>
</ClientTabsContent>
<ClientTabsContent value={"windows"}>
<ClipboardField
variant="primary/medium"
fullWidth={false}
value={`choco install ngrok`}
/>
</ClientTabsContent>
</ClientTabs>
</StepContentContainer>
<StepNumber stepNumber="b" title="Open a new terminal window/tab" />
<StepContentContainer>
<Paragraph spacing>You need to leave this running.</Paragraph>
</StepContentContainer>
<StepNumber stepNumber="c" title="Create an http tunnel at port 3000" />
<StepContentContainer>
<Paragraph spacing>
This creates a tunnel to your Next.js app.
</Paragraph>
<ClipboardField
variant="primary/medium"
fullWidth={false}
className="mb-4"
value={`ngrok http 3000`}
/>
</StepContentContainer>
<StepNumber
stepNumber="d"
title={`Grab your "forwarding" URL from the ngrok output`}
/>
<StepContentContainer>
<img src={ngrok} className="mt-2 w-full" />
</StepContentContainer>
</StepContentContainer>
<StepNumber stepNumber="3" title="Use the CLI" />
<StepContentContainer>
<Paragraph spacing>
Copy this CLI command into a new terminal window.{" "}
</Paragraph>
<ClientTabs defaultValue="npm">
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
@@ -102,46 +46,98 @@ export function HowToSetupYourProject() {
<ClipboardField
variant="primary/medium"
className="mb-4"
secure="npx @trigger.dev/init@latest -k ••••••••• -t https://cloud.trigger.dev -u <ngrok_forwarding_url>"
value={`npx @trigger.dev/init@latest -k ${devEnvironment?.apiKey} -t https://cloud.trigger.dev -u <ngrok_forwarding_url>`}
secure={`npx @trigger.dev/cli@latest init -k ••••••••• -t ${appOrigin}`}
value={`npx @trigger.dev/cli@latest init -k ${devEnvironment?.apiKey} -t ${appOrigin}`}
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="primary/medium"
className="mb-4"
secure="pnpm dlx @trigger.dev/init@latest -k ••••••••• -t https://cloud.trigger.dev -u <ngrok_forwarding_url>"
value={`pnpm dlx @trigger.dev/init@latest -k ${devEnvironment?.apiKey} -t https://cloud.trigger.dev -u <ngrok_forwarding_url>`}
secure={`pnpm dlx @trigger.dev/cli@latest init -k ••••••••• -t ${appOrigin}`}
value={`pnpm dlx @trigger.dev/cli@latest init -k ${devEnvironment?.apiKey} -t ${appOrigin}`}
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="primary/medium"
className="mb-4"
secure="yarn @trigger.dev/init@latest -k ••••••••• -t https://cloud.trigger.dev -u <ngrok_forwarding_url>"
value={`yarn @trigger.dev/init@latest -k ${devEnvironment?.apiKey} -t https://cloud.trigger.dev -u <ngrok_forwarding_url>`}
secure={`yarn dlx @trigger.dev/cli@latest init -k ••••••••• -t ${appOrigin}`}
value={`yarn dlx @trigger.dev/cli@latest init -k ${devEnvironment?.apiKey} -t ${appOrigin}`}
/>
</ClientTabsContent>
</ClientTabs>
<Paragraph spacing>
Use the "forwarding" URL from step 2d above to replace the -u
placeholder text.
</Paragraph>
<img src={publicUrl} className="mb-4 mt-2 w-full" />
</StepContentContainer>
<StepNumber stepNumber="4" title="Run the CLI" />
<Paragraph spacing>
Youll notice a new folder in your project called 'jobs'. Weve added a very simple
example Job in <InlineCode>examples.ts</InlineCode> to help you get started.
</Paragraph>
</StepContentContainer>
<StepNumber stepNumber="2" title="Run your Next.js app" />
<StepContentContainer>
<Paragraph>Ensure your app is running locally.</Paragraph>
<ClientTabs defaultValue="npm">
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<ClientTabsContent value={"npm"}>
<ClipboardField variant="primary/medium" className="mb-4" value={`npm run dev`} />
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField variant="primary/medium" className="mb-4" value={`pnpm run dev`} />
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField variant="primary/medium" className="mb-4" value={`yarn run dev`} />
</ClientTabsContent>
</ClientTabs>
</StepContentContainer>
<StepNumber stepNumber="3" title="Run the CLI 'dev' command" />
<StepContentContainer>
<Paragraph spacing>
It will add Trigger.dev to your existing Next.js project, setup a
route and give you an example file.
In a <strong className="text-bright">separate terminal window or tab</strong> run:
</Paragraph>
<ClientTabs defaultValue="npm">
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<ClientTabsContent value={"npm"}>
<ClipboardField
variant="primary/medium"
className="mb-4"
value={`npx @trigger.dev/cli@latest dev`}
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="primary/medium"
className="mb-4"
value={`pnpm dlx @trigger.dev/cli@latest dev`}
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="primary/medium"
className="mb-4"
value={`yarn dlx @trigger.dev/cli@latest dev`}
/>
</ClientTabsContent>
</ClientTabs>
<Paragraph spacing variant="small">
If youre not running on port 3000 you can specify the port by adding{" "}
<InlineCode>--port 3001</InlineCode> to the end.
</Paragraph>
<Paragraph spacing variant="small">
You should leave the <InlineCode>dev</InlineCode> command running when you're developing.
</Paragraph>
</StepContentContainer>
<StepNumber stepNumber="5" title="Check for Jobs" />
<StepNumber stepNumber="4" title="Check for Jobs" />
<StepContentContainer>
<Paragraph>
Once you've run the CLI command, click Refresh to view your example
Job in the list.
Once you've run the CLI command, click Refresh to view your example Job in the list.
</Paragraph>
<Button
variant="primary/medium"
@@ -156,13 +152,91 @@ export function HowToSetupYourProject() {
);
}
export function HowToRunYourJob() {
const organization = useOrganization();
const project = useProject();
const job = useJob();
return (
<>
<Callout variant="info" className="mb-6">
<Paragraph variant={"small"} className={variantClasses.info.textColor}>
Scheduled Triggers <strong>do not</strong> trigger Jobs in the DEV Environment. When
developing locally you should use the{" "}
<Link
to={jobTestPath(organization, project, job)}
className="underline underline-offset-2 transition hover:text-blue-100"
>
Test feature
</Link>{" "}
to trigger any scheduled Jobs.
</Paragraph>
</Callout>
<Paragraph spacing>There are two ways to run your Job:</Paragraph>
<StepNumber stepNumber="1" title="Trigger a test Run" />
<StepContentContainer>
<Paragraph spacing>
You can perform a Run with any payload you want, or use one of our examples, on the test
page.
</Paragraph>
<LinkButton
to={jobTestPath(organization, project, job)}
variant={"primary/small"}
LeadingIcon={"beaker"}
leadingIconClassName="text-bright"
>
Test
</LinkButton>
</StepContentContainer>
<StepNumber stepNumber="2" title="Trigger your Job for real" />
<StepContentContainer>
<Paragraph spacing>
Performing a real run depends on the type of Trigger your Job is using.
</Paragraph>
<LinkButton
to="https://trigger.dev/docs/documentation/guides/running-jobs"
variant={"primary/small"}
LeadingIcon={"docs"}
leadingIconClassName="text-bright"
>
How to run a Job
</LinkButton>
</StepContentContainer>
</>
);
}
export function HowToRunATest() {
return (
<>
<StepNumber stepNumber="1" title="Step 1 title" />
<StepNumber
stepNumber="1"
title="Select an environment
"
/>
<StepContentContainer>
<Paragraph>Content</Paragraph>
<Paragraph spacing>Select the environment youd like the test to run against.</Paragraph>
<img src={selectEnvironment} className="mt-2 w-52" />
</StepContentContainer>
<StepNumber stepNumber="2" title="Write your test payload" />
<StepContentContainer>
<Paragraph spacing>
Write your own payload specific to your Job. Some Triggers also provide example payloads
that you can select from. This will populate the code editor below.
</Paragraph>
<img src={selectExample} className="mt-2 h-40" />
</StepContentContainer>
<StepNumber stepNumber="3" title="Run your test" />
<StepContentContainer>
<Paragraph spacing>When youre happy with the payload, click Run test.</Paragraph>
</StepContentContainer>
<Callout variant="docs" to="https://trigger.dev/docs/documentation/guides/testing-jobs">
Learn more about running tests.
</Callout>
</>
);
}
@@ -174,55 +248,142 @@ export function HowToConnectAnIntegration() {
<StepContentContainer>
<Paragraph>
APIs marked with a
<span
className="mx-2 -mt-1 inline-flex"
aria-label="Trigger.dev Integration icon"
>
<span className="mx-2 -mt-1 inline-flex" aria-label="Trigger.dev Integration icon">
<IntegrationIcon />
</span>
are Trigger.dev Integrations. These Integrations make connecting to
the API easier by offering OAuth or API key authentication. All APIs
can also be used with fetch or an SDK.
are Trigger.dev Integrations. These Integrations make connecting to the API easier by
offering OAuth or API key authentication. All APIs can also be used with fetch or an SDK.
</Paragraph>
<img src={integrationButton} className="mt-2 h-10" />
</StepContentContainer>
<StepNumber stepNumber="2" title="Choose how you want to connect" />
<StepContentContainer>
<Paragraph>
Follow the instructions for your chosen connection method in the
popover form. If no Integration exists yet, you can request one by
clicking the "I want an Integration" button.
Follow the instructions for your chosen connection method in the popover form. If no
Integration exists yet, you can request one by clicking the "I want an Integration"
button.
</Paragraph>
</StepContentContainer>
<StepNumber
stepNumber="3"
title="Your connection will appear in the list"
/>
<StepNumber stepNumber="3" title="Your connection will appear in the list" />
<StepContentContainer>
<Paragraph>
Once you've connected your API, it will appear in the list of
Integrations below. You can view details and manage your connection by
selecting it from the table.
Once you've connected your API, it will appear in the list of Integrations below. You can
view details and manage your connection by selecting it from the table.
</Paragraph>
</StepContentContainer>
<Callout
variant={"docs"}
href="https://trigger.dev/docs/integrations/introduction"
>
View the Integration docs page for more information on connecting an API
using an Integration or other method.
<Callout variant={"docs"} to="https://trigger.dev/docs/integrations/introduction">
View the Integration docs page for more information on connecting an API using an
Integration or other method.
</Callout>
</>
);
}
export function HowToUseThisIntegration() {
export function HowToUseThisIntegration({ integration, help, integrationClient }: HelpPanelProps) {
return (
<>
<StepNumber stepNumber="1" title="Step 1 title" />
<StepNumber stepNumber="1" title="Install the package" />
<StepContentContainer>
<Paragraph>Content</Paragraph>
<HelpInstall packageName={integration.packageName} />
</StepContentContainer>
{help && (
<>
<StepNumber stepNumber="2" title="Create a Job" />
<StepContentContainer>
<HelpSamples
help={help}
integration={integration}
integrationClient={integrationClient}
/>
</StepContentContainer>
</>
)}
</>
);
}
export function HowToUseApiKeysAndEndpoints() {
return (
<>
<Paragraph spacing>
Environments and Endpoints are used to connect your server to the Trigger.dev platform.
</Paragraph>
<Header2 spacing>Environments</Header2>
<Paragraph spacing>
Each environment has API Keys associated with it. The Server API Key is used to authenticate
your Jobs with the Trigger.dev platform.
</Paragraph>
<Paragraph spacing>
The Server API Key you use for your{" "}
<TextLink to="https://trigger.dev/docs/documentation/concepts/client-adaptors">
Client
</TextLink>{" "}
is how we know which environment to run your code against:
</Paragraph>
<CodeBlock
showLineNumbers={false}
className="mb-4"
code={`export const client = new TriggerClient({
id: "nextjs-example",
//this environment variable should be set to your Server DEV API Key locally,
//and your Server PROD API Key in production
apiKey: process.env.TRIGGER_API_KEY!,
});`}
/>
<StepNumber
stepNumber="→"
title={
<span className="flex items-center gap-x-2">
<span>Development</span>
<EnvironmentLabel environment={{ type: "DEVELOPMENT" }} />
</span>
}
/>
<StepContentContainer>
<Paragraph>
The <InlineCode>DEV</InlineCode> environment should only be used for local development.
Its where you can test your Jobs before deploying them to servers.
</Paragraph>
<Callout variant="warning" className="my-2">
Scheduled Triggers do not trigger Jobs in the DEV Environment. When youre working locally
you should use the Test feature to trigger any scheduled Jobs.
</Callout>
</StepContentContainer>
<StepNumber
stepNumber="→"
title={
<span className="flex items-center gap-x-2">
<span>Production</span>
<EnvironmentLabel environment={{ type: "PRODUCTION" }} />
</span>
}
/>
<StepContentContainer>
<Paragraph spacing>
The <InlineCode>PROD</InlineCode> environment is where your Jobs will run in production.
Its where you can run your Jobs against real data.
</Paragraph>
</StepContentContainer>
<Header2 spacing>Endpoints</Header2>
<Paragraph spacing>
An Endpoint is a URL on your server that Trigger.dev can connect to. This URL is used to
register Jobs, start them and orchestrate runs and retries.
</Paragraph>
<Paragraph spacing>
<InlineCode>DEV</InlineCode> has multiple endpoints associated with it one for each team
member. This allows each team member to run their own Jobs, without interfering with each
other.
</Paragraph>
<Paragraph spacing>
All other environments have just a single endpoint (with a single URL) associated with them.
</Paragraph>
<Header2 spacing>Deployment</Header2>
<Paragraph spacing>
Deployment uses Environments and Endpoints to connect your Jobs to the Trigger.dev platform.
</Paragraph>
<Callout variant="docs" to="https://trigger.dev/docs/documentation/guides/deployment">
Read the deployment guide to learn more.
</Callout>
</>
);
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -1,48 +1,34 @@
import {
ApiAuthenticationMethodApiKey,
Integration,
} from "~/services/externalApis/types";
import { Paragraph } from "../primitives/Paragraph";
import { CodeBlock } from "../code/CodeBlock";
import { Help, Integration } from "~/services/externalApis/types";
import { InlineCode } from "../code/InlineCode";
import { Header1, Header2 } from "../primitives/Headers";
import { ClipboardField } from "../primitives/ClipboardField";
import { Header1 } from "../primitives/Headers";
import { Paragraph } from "../primitives/Paragraph";
import { HelpInstall } from "./HelpInstall";
import { HelpSamples, ReplacementData } from "./HelpSamples";
export function ApiKeyHelp({
integration,
apiAuth,
}: {
integration: Integration;
apiAuth: ApiAuthenticationMethodApiKey;
}) {
export type HelpPanelIntegration = Pick<Integration, "name" | "packageName" | "identifier">;
export type HelpPanelProps = {
integration: HelpPanelIntegration;
help?: Help;
integrationClient?: ReplacementData;
};
export function ApiKeyHelp({ integration, help, integrationClient }: HelpPanelProps) {
return (
<div className="mt-4">
<Header1 className="mb-2">
How to use {integration.name} with API keys
</Header1>
<Header1 className="mb-2">How to use {integration.name} with API keys</Header1>
<Paragraph spacing>
You can use API keys to authenticate with {integration.name}. Your API
keys won't leave your server, we'll never see them.
You can use API keys to authenticate with {integration.name}. Your API keys won't leave your
server, we'll never see them.
</Paragraph>
<Paragraph spacing>
First install the <InlineCode>{integration.packageName}</InlineCode>{" "}
package using your preferred package manager. For example:
First install the <InlineCode>{integration.packageName}</InlineCode> package using your
preferred package manager. For example:
</Paragraph>
<ClipboardField
variant="secondary/medium"
value={`npm install ${integration.packageName}`}
className="mb-4"
/>
{apiAuth.help.samples.map((sample, i) => (
<div key={i}>
<Paragraph spacing>{sample.title}</Paragraph>
<CodeBlock
code={sample.code}
className="mb-4"
highlightedRanges={sample.highlight}
/>
</div>
))}
<HelpInstall packageName={integration.packageName} />
{help && (
<HelpSamples help={help} integration={integration} integrationClient={integrationClient} />
)}
</div>
);
}
@@ -1,21 +1,12 @@
import React, { useState } from "react";
import {
ApiAuthenticationMethodApiKey,
Integration,
} from "~/services/externalApis/types";
import { ApiAuthenticationMethodApiKey, Integration } from "~/services/externalApis/types";
import { docsIntegrationPath } from "~/utils/pathBuilder";
import { LinkButton } from "../primitives/Buttons";
import { Header2 } from "../primitives/Headers";
import { Header1, Header2 } from "../primitives/Headers";
import { NamedIconInBox } from "../primitives/NamedIcon";
import { Paragraph } from "../primitives/Paragraph";
import { RadioGroup, RadioGroupItem } from "../primitives/RadioButton";
import {
Sheet,
SheetBody,
SheetContent,
SheetHeader,
SheetTrigger,
} from "../primitives/Sheet";
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
import { ApiKeyHelp } from "./ApiKeyHelp";
import { CustomHelp } from "./CustomHelp";
import { SelectOAuthMethod } from "./SelectOAuthMethod";
@@ -28,16 +19,18 @@ export function ConnectToIntegrationSheet({
button,
className,
callbackUrl,
icon,
}: {
integration: Integration;
organizationId: string;
button: React.ReactNode;
callbackUrl: string;
className?: string;
icon?: string;
}) {
const [integrationMethod, setIntegrationMethod] = useState<
IntegrationMethod | undefined
>(undefined);
const [integrationMethod, setIntegrationMethod] = useState<IntegrationMethod | undefined>(
undefined
);
const authMethods = Object.values(integration.authenticationMethods);
const hasApiKeyOption = authMethods.some((s) => s.type === "apikey");
@@ -48,9 +41,9 @@ export function ConnectToIntegrationSheet({
<SheetTrigger className={className}>{button}</SheetTrigger>
<SheetContent size="lg" className="relative">
<SheetHeader>
<NamedIconInBox name={integration.identifier} className="h-9 w-9" />
<NamedIconInBox name={icon ?? integration.identifier} className="h-9 w-9" />
<div className="grow">
<Header2>{integration.name}</Header2>
<Header1>{integration.name}</Header1>
{integration.description && (
<Paragraph variant="small">{integration.description}</Paragraph>
)}
@@ -59,6 +52,7 @@ export function ConnectToIntegrationSheet({
to={docsIntegrationPath(integration.identifier)}
variant="secondary/small"
LeadingIcon="docs"
target="_blank"
>
View docs
</LinkButton>
@@ -129,12 +123,7 @@ function SelectedIntegrationMethod({
case "apikey":
const apiAuth = authMethods.find((a) => a.type === "apikey");
if (!apiAuth) return null;
return (
<ApiKeyHelp
integration={integration}
apiAuth={apiAuth as ApiAuthenticationMethodApiKey}
/>
);
return <ApiKeyHelp integration={integration} help={apiAuth.help} />;
case "oauth2":
return (
<SelectOAuthMethod
@@ -8,11 +8,7 @@ import simplur from "simplur";
import { useFeatures } from "~/hooks/useFeatures";
import { useTextFilter } from "~/hooks/useTextFilter";
import { createSchema } from "~/routes/resources.connection.$organizationId.oauth2";
import {
ApiAuthenticationMethodOAuth2,
Integration,
Scope,
} from "~/services/externalApis/types";
import { ApiAuthenticationMethodOAuth2, Integration, Scope } from "~/services/externalApis/types";
import { cn } from "~/utils/cn";
import { CodeBlock } from "../code/CodeBlock";
import { Button } from "../primitives/Buttons";
@@ -48,32 +44,22 @@ export function ConnectToOAuthForm({
const fetcher = useFetcher();
const { isManagedCloud } = useFeatures();
const [
form,
{
title,
slug,
scopes,
hasCustomClient,
customClientId,
customClientSecret,
},
] = useForm({
lastSubmission: fetcher.data,
onValidate({ formData }) {
return parse(formData, {
// Create the schema without any constraint defined
schema: createSchema(),
});
},
});
const [form, { title, slug, scopes, hasCustomClient, customClientId, customClientSecret }] =
useForm({
lastSubmission: fetcher.data,
shouldRevalidate: "onSubmit",
onValidate({ formData }) {
return parse(formData, {
// Create the schema without any constraint defined
schema: createSchema(),
});
},
});
const location = useLocation();
const [selectedScopes, setSelectedScopes] = useState<Set<string>>(
new Set(
authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name)
)
new Set(authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name))
);
const requiresCustomOAuthApp = clientType === "EXTERNAL" || !isManagedCloud;
@@ -84,10 +70,7 @@ export function ConnectToOAuthForm({
items: authMethod.scopes,
filter: (scope, text) => {
if (scope.name.toLowerCase().includes(text.toLowerCase())) return true;
if (
scope.description &&
scope.description.toLowerCase().includes(text.toLowerCase())
)
if (scope.description && scope.description.toLowerCase().includes(text.toLowerCase()))
return true;
return false;
@@ -103,16 +86,8 @@ export function ConnectToOAuthForm({
>
<Fieldset>
<input type="hidden" name="id" value={id} />
<input
type="hidden"
name="integrationIdentifier"
value={integration.identifier}
/>
<input
type="hidden"
name="integrationAuthMethod"
value={authMethodKey}
/>
<input type="hidden" name="integrationIdentifier" value={integration.identifier} />
<input type="hidden" name="integrationAuthMethod" value={authMethodKey} />
<input type="hidden" name="redirectTo" value={location.pathname} />
<InputGroup>
<FormError>{form.error}</FormError>
@@ -126,8 +101,8 @@ export function ConnectToOAuthForm({
placeholder={`e.g. ${integration.identifier}`}
/>
<Hint>
This is used in your code to reference this connection. It must be
unique for this project.
This is used in your code to reference this connection. It must be unique for this
project.
</Hint>
<FormError>{slug.error}</FormError>
</InputGroup>
@@ -145,8 +120,7 @@ export function ConnectToOAuthForm({
<div>
<Header2>Use my OAuth App</Header2>
<Paragraph variant="small" className="mb-2">
To use your own OAuth app, check the option below and insert the
details.
To use your own OAuth app, check the option below and insert the details.
</Paragraph>
<Checkbox
id="hasCustomClient"
@@ -160,17 +134,13 @@ export function ConnectToOAuthForm({
{useMyOAuthApp && (
<div className="ml-6 mt-2">
<Paragraph variant="small" className="mb-2">
Set the callback url to{" "}
<CodeBlock code={callbackUrl} showLineNumbers={false} />
Set the callback url to <CodeBlock code={callbackUrl} showLineNumbers={false} />
</Paragraph>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<InputGroup fullWidth>
<Label variant="small">Client ID</Label>
<Input
fullWidth
{...conform.input(customClientId, { type: "text" })}
/>
<Input fullWidth {...conform.input(customClientId, { type: "text" })} />
</InputGroup>
<InputGroup fullWidth>
<Label variant="small">Client secret</Label>
@@ -187,15 +157,15 @@ export function ConnectToOAuthForm({
</div>
)}
</div>
<div>
<Header2>Scopes</Header2>
<Paragraph variant="small" className="mb-4">
Select the scopes you want to grant to {integration.name} in order
for it to access your data. Note: If you try and perform an action
in a Job that requires a scope you havent granted, that task will
fail.
</Paragraph>
{/* <Header3 className="mb-2">
{authMethod.scopes.length > 0 && (
<div>
<Header2>Scopes</Header2>
<Paragraph variant="small" className="mb-4">
Select the scopes you want to grant to {integration.name} in order for it to access
your data. Note: If you try and perform an action in a Job that requires a scope you
havent granted, that task will fail.
</Paragraph>
{/* <Header3 className="mb-2">
Select from popular scope collections
</Header3>
<fieldset>
@@ -205,60 +175,59 @@ export function ConnectToOAuthForm({
variant="button/small"
/>
</fieldset> */}
<div className="mb-2 mt-4 flex items-center justify-between">
<Header3>Select {integration.name} scopes</Header3>
<Paragraph variant="small" className="text-slate-500">
{simplur`${selectedScopes.size} scope[|s] selected`}
</Paragraph>
</div>
<Input
placeholder="Search scopes"
className="mb-2"
variant="medium"
icon="search"
fullWidth={true}
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
/>
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
{filteredItems.length === 0 && (
<Paragraph variant="small" className="p-4">
No scopes match {filterText}. Try a different search query.
<div className="mb-2 mt-4 flex items-center justify-between">
<Header3>Select {integration.name} scopes</Header3>
<Paragraph variant="small" className="text-slate-500">
{simplur`${selectedScopes.size} scope[|s] selected`}
</Paragraph>
)}
{authMethod.scopes.map((s) => {
return (
<Checkbox
key={s.name}
id={s.name}
value={s.name}
name="scopes"
label={s.name}
defaultChecked={s.defaultChecked ?? false}
badges={s.annotations?.map((a) => a.label)}
description={s.description}
variant="description"
className={cn(
filteredItems.find((f) => f.name === s.name) ? "" : "hidden"
)}
onChange={(isChecked) => {
if (isChecked) {
setSelectedScopes((selected) => {
selected.add(s.name);
return new Set(selected);
});
} else {
setSelectedScopes((selected) => {
selected.delete(s.name);
return new Set(selected);
});
}
}}
/>
);
})}
</div>
<Input
placeholder="Search scopes"
className="mb-2"
variant="medium"
icon="search"
fullWidth={true}
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
/>
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
{filteredItems.length === 0 && (
<Paragraph variant="small" className="p-4">
No scopes match {filterText}. Try a different search query.
</Paragraph>
)}
{authMethod.scopes.map((s) => {
return (
<Checkbox
key={s.name}
id={s.name}
value={s.name}
name="scopes"
label={s.name}
defaultChecked={s.defaultChecked ?? false}
badges={s.annotations?.map((a) => a.label)}
description={s.description}
variant="description"
className={cn(filteredItems.find((f) => f.name === s.name) ? "" : "hidden")}
onChange={(isChecked) => {
if (isChecked) {
setSelectedScopes((selected) => {
selected.add(s.name);
return new Set(selected);
});
} else {
setSelectedScopes((selected) => {
selected.delete(s.name);
return new Set(selected);
});
}
}}
/>
);
})}
</div>
</div>
</div>
)}
</Fieldset>
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
@@ -268,7 +237,7 @@ export function ConnectToOAuthForm({
className="flex gap-2"
disabled={transition.state !== "idle"}
variant="primary/medium"
LeadingIcon={integration.identifier}
LeadingIcon={integration.icon ?? integration.identifier}
>
Connect to {integration.name}
</Button>
@@ -5,20 +5,16 @@ import { Paragraph } from "../primitives/Paragraph";
export function CustomHelp({ name }: { name: string }) {
return (
<div className="mt-4">
<Header1 className="mb-2">
You can use any API with requests or an SDK
</Header1>
<Header1 className="mb-2">You can use any API with requests or an SDK</Header1>
<Header2 className="mb-2">How to use an SDK</Header2>
<Paragraph spacing>
You can call SDK methods from inside the run function, but you should
wrap them in a Task to make sure they're resumable.
</Paragraph>
<Paragraph spacing>
Here's an example with the official GitHub SDK
You can call SDK methods from inside the run function, but you should wrap them in a Task to
make sure they're resumable.
</Paragraph>
<Paragraph spacing>Here's an example with the official GitHub SDK</Paragraph>
<CodeBlock
code={`
new Job(client, {
client.defineJob({
id: "scheduled-job-1",
name: "Scheduled Job 1",
version: "0.1.1",
@@ -48,13 +44,12 @@ new Job(client, {
/>
<Header2 className="mb-2">How to use fetch</Header2>
<Paragraph spacing>
You can use the fetch API to make requests to any API. Or a different
request library like axios if you'd prefer. Again wrapping the request
in a Task will make sure it's resumable.
You can use the fetch API to make requests to any API. Or a different request library like
axios if you'd prefer. Again wrapping the request in a Task will make sure it's resumable.
</Paragraph>
<CodeBlock
code={`
new Job(client, {
client.defineJob({
id: "scheduled-job-1",
name: "Scheduled Job 1",
version: "0.1.1",
@@ -0,0 +1,45 @@
import { Integration } from "~/services/externalApis/types";
import { InlineCode } from "../code/InlineCode";
import {
ClientTabs,
ClientTabsList,
ClientTabsTrigger,
ClientTabsContent,
} from "../primitives/ClientTabs";
import { ClipboardField } from "../primitives/ClipboardField";
import { Paragraph } from "../primitives/Paragraph";
export function HelpInstall({ packageName }: { packageName: string }) {
return (
<>
<ClientTabs defaultValue="npm">
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<ClientTabsContent value={"npm"}>
<ClipboardField
variant="secondary/medium"
value={`npm install ${packageName}`}
className="mb-4"
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="secondary/medium"
value={`pnpm add ${packageName}`}
className="mb-4"
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="secondary/medium"
value={`yarn add ${packageName}`}
className="mb-4"
/>
</ClientTabsContent>
</ClientTabs>
</>
);
}
@@ -0,0 +1,47 @@
import { Integration } from "~/services/externalApis/types";
import { HelpPanelIntegration, HelpPanelProps } from "./ApiKeyHelp";
import { Paragraph } from "../primitives/Paragraph";
import { CodeBlock } from "../code/CodeBlock";
export type ReplacementData = {
slug: string;
};
export function HelpSamples({ help, integrationClient, integration }: HelpPanelProps) {
return (
<>
{help &&
help.samples.map((sample, i) => {
const code = runReplacers(sample.code, integrationClient, integration);
return (
<div key={i}>
<Paragraph spacing>{sample.title}</Paragraph>
<CodeBlock code={code} className="mb-4" highlightedRanges={sample.highlight} />
</div>
);
})}
</>
);
}
const replacements = [
{
match: /__SLUG__/g,
replacement: (data: ReplacementData | undefined, integration: HelpPanelIntegration) => {
if (data) return data.slug;
return integration.identifier;
},
},
];
function runReplacers(
code: string,
replacementData: ReplacementData | undefined,
integration: HelpPanelIntegration
) {
replacements.forEach((r) => {
code = code.replace(r.match, r.replacement(replacementData, integration));
});
return code;
}
@@ -0,0 +1,58 @@
import { docsIntegrationPath } from "~/utils/pathBuilder";
import { LinkButton } from "../primitives/Buttons";
import { Header1 } from "../primitives/Headers";
import { NamedIconInBox } from "../primitives/NamedIcon";
import { Paragraph } from "../primitives/Paragraph";
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
import { SelectOAuthMethod } from "./SelectOAuthMethod";
import { Integration } from "~/services/externalApis/types";
import { Client } from "~/presenters/IntegrationsPresenter.server";
export function IntegrationWithMissingFieldSheet({
integration,
organizationId,
button,
callbackUrl,
existingIntegration,
className,
}: {
integration: Integration;
organizationId: string;
button: React.ReactNode;
callbackUrl: string;
existingIntegration: Client;
className?: string;
}) {
return (
<Sheet>
<SheetTrigger className={className}>{button}</SheetTrigger>
<SheetContent size="lg" className="relative">
<SheetHeader>
<NamedIconInBox name={integration.identifier} className="h-9 w-9" />
<div className="grow">
<Header1>{integration.name}</Header1>
{integration.description && (
<Paragraph variant="small">{integration.description}</Paragraph>
)}
</div>
<LinkButton
to={docsIntegrationPath(integration.identifier)}
variant="secondary/small"
LeadingIcon="docs"
target="_blank"
>
View docs
</LinkButton>
</SheetHeader>
<SheetBody>
<SelectOAuthMethod
integration={integration}
organizationId={organizationId}
callbackUrl={callbackUrl}
existingIntegration={existingIntegration}
/>
</SheetBody>
</SheetContent>
</Sheet>
);
}
@@ -4,13 +4,7 @@ import { Button } from "../primitives/Buttons";
import { Callout } from "../primitives/Callout";
import { Header1 } from "../primitives/Headers";
import { NamedIconInBox } from "../primitives/NamedIcon";
import {
Sheet,
SheetBody,
SheetContent,
SheetHeader,
SheetTrigger,
} from "../primitives/Sheet";
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
import { CustomHelp } from "./CustomHelp";
import { CheckIcon } from "@heroicons/react/24/solid";
import { useFetcher } from "@remix-run/react";
@@ -41,32 +35,22 @@ export function NoIntegrationSheet({
<div className="flex items-center gap-1">
<CheckIcon className="h-4 w-4 text-green-500" />
<Paragraph variant="small">
We'll let you know when the integration is available.
We'll let you know when the Integration is available.
</Paragraph>
</div>
) : (
<fetcher.Form
method="post"
action={`/resources/apivote/${api.identifier}`}
>
<fetcher.Form method="post" action={`/resources/apivote/${api.identifier}`}>
<Button
variant="primary/small"
disabled={isLoading}
LeadingIcon={isLoading ? "spinner-white" : undefined}
>
{isLoading
? "Saving…"
: `I want an integration for ${api.name}`}
{isLoading ? "Saving…" : `I want an Integration for ${api.name}`}
</Button>
</fetcher.Form>
)}
</SheetHeader>
<SheetBody>
<Callout variant="info">
We dont have an integration for the {api.name} API yet but you can
request one by clicking the button above. In the meantime, connect
to {api.name} using the methods below.
</Callout>
<CustomHelp name={api.name} />
</SheetBody>
</SheetContent>
@@ -1,19 +1,20 @@
import { useState } from "react";
import {
ApiAuthenticationMethodOAuth2,
Integration,
} from "~/services/externalApis/types";
import { ApiAuthenticationMethodOAuth2, Integration } from "~/services/externalApis/types";
import { RadioGroup, RadioGroupItem } from "../primitives/RadioButton";
import type { ConnectionType } from "@trigger.dev/database";
import { Header2 } from "../primitives/Headers";
import { ConnectToOAuthForm } from "./ConnectToOAuthForm";
import { Paragraph } from "../primitives/Paragraph";
import { Client } from "~/presenters/IntegrationsPresenter.server";
import { UpdateOAuthForm } from "./UpdateOAuthForm";
export function SelectOAuthMethod({
integration,
organizationId,
callbackUrl
callbackUrl,
existingIntegration,
}: {
existingIntegration?: Client;
integration: Integration;
organizationId: string;
callbackUrl: string;
@@ -25,14 +26,10 @@ export function SelectOAuthMethod({
const [oAuthKey, setOAuthKey] = useState<string | undefined>(
oAuthMethods.length === 1 ? oAuthMethods[0][0] : undefined
);
const [connectionType, setConnectionType] = useState<
ConnectionType | undefined
>();
const [connectionType, setConnectionType] = useState<ConnectionType | undefined>();
const selectedOAuthMethod = oAuthKey
? (integration.authenticationMethods[
oAuthKey
] as ApiAuthenticationMethodOAuth2)
? (integration.authenticationMethods[oAuthKey] as ApiAuthenticationMethodOAuth2)
: undefined;
return (
@@ -61,9 +58,7 @@ export function SelectOAuthMethod({
)}
{selectedOAuthMethod && (
<>
<Header2 className="mb-2 mt-4">
Who is connecting to {integration.name}
</Header2>
<Header2 className="mb-2 mt-4">Who is connecting to {integration.name}</Header2>
<RadioGroup
name="connection-type"
className="flex gap-2"
@@ -91,24 +86,33 @@ export function SelectOAuthMethod({
connectionType &&
oAuthKey &&
(connectionType === "DEVELOPER" ? (
<ConnectToOAuthForm
integration={integration}
authMethod={selectedOAuthMethod}
authMethodKey={oAuthKey}
organizationId={organizationId}
clientType={connectionType}
callbackUrl={callbackUrl}
/>
existingIntegration ? (
<UpdateOAuthForm
existingIntegration={existingIntegration}
integration={integration}
authMethod={selectedOAuthMethod}
authMethodKey={oAuthKey}
organizationId={organizationId}
clientType={connectionType}
callbackUrl={callbackUrl}
/>
) : (
<ConnectToOAuthForm
integration={integration}
authMethod={selectedOAuthMethod}
authMethodKey={oAuthKey}
organizationId={organizationId}
clientType={connectionType}
callbackUrl={callbackUrl}
/>
)
) : (
<>
<Header2 className="mb-1 mt-4">User OAuth coming soon</Header2>
<Paragraph spacing>
End-user OAuth is going to be released soon. If you are interested
in being an early beta tester then please{" "}
<a
href="mailto:founders@trigger.dev"
className="text-indigo-500 underline"
>
End-user OAuth is going to be released soon. If you are interested in being an early
beta tester then please{" "}
<a href="mailto:founders@trigger.dev" className="text-indigo-500 underline">
message us
</a>
.
@@ -0,0 +1,237 @@
import { conform, useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { useFetcher, useLocation, useNavigation } from "@remix-run/react";
import type { ConnectionType } from "@trigger.dev/database";
import { useState } from "react";
import simplur from "simplur";
import { useFeatures } from "~/hooks/useFeatures";
import { useTextFilter } from "~/hooks/useTextFilter";
import { ApiAuthenticationMethodOAuth2, Integration, Scope } from "~/services/externalApis/types";
import { cn } from "~/utils/cn";
import { CodeBlock } from "../code/CodeBlock";
import { Button } from "../primitives/Buttons";
import { Checkbox } from "../primitives/Checkbox";
import { Fieldset } from "../primitives/Fieldset";
import { FormError } from "../primitives/FormError";
import { Header2, Header3 } from "../primitives/Headers";
import { Input } from "../primitives/Input";
import { InputGroup } from "../primitives/InputGroup";
import { Label } from "../primitives/Label";
import { Paragraph } from "../primitives/Paragraph";
import { Client } from "~/presenters/IntegrationsPresenter.server";
import { schema } from "~/routes/resources.connection.$organizationId.oauth2.$integrationId";
export type Status = "loading" | "idle";
export function UpdateOAuthForm({
existingIntegration,
integration,
authMethod,
authMethodKey,
organizationId,
clientType,
callbackUrl,
}: {
existingIntegration: Client;
integration: Integration;
authMethod: ApiAuthenticationMethodOAuth2;
authMethodKey: string;
organizationId: string;
clientType: ConnectionType;
callbackUrl: string;
}) {
const transition = useNavigation();
const fetcher = useFetcher();
const { isManagedCloud } = useFeatures();
const [form, { title, scopes, hasCustomClient, customClientId, customClientSecret }] = useForm({
lastSubmission: fetcher.data,
onValidate({ formData }) {
return parse(formData, {
schema,
});
},
});
const location = useLocation();
const [selectedScopes, setSelectedScopes] = useState<Set<string>>(
new Set(authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name))
);
const requiresCustomOAuthApp = clientType === "EXTERNAL" || !isManagedCloud;
const [useMyOAuthApp, setUseMyOAuthApp] = useState(requiresCustomOAuthApp);
const { filterText, setFilterText, filteredItems } = useTextFilter<Scope>({
items: authMethod.scopes,
filter: (scope, text) => {
if (scope.name.toLowerCase().includes(text.toLowerCase())) return true;
if (scope.description && scope.description.toLowerCase().includes(text.toLowerCase()))
return true;
return false;
},
});
return (
<fetcher.Form
method="put"
action={`/resources/connection/${organizationId}/oauth2/${existingIntegration.id}`}
{...form.props}
className="flex h-full max-h-full flex-grow flex-col"
>
<Fieldset>
<input type="hidden" name="id" value={existingIntegration.id} />
<input type="hidden" name="integrationIdentifier" value={integration.identifier} />
<input type="hidden" name="integrationAuthMethod" value={authMethodKey} />
<input type="hidden" name="redirectTo" value={location.pathname} />
<InputGroup>
<FormError>{form.error}</FormError>
</InputGroup>
<InputGroup fullWidth>
<Label variant="large">ID</Label>
<Paragraph variant="small" className="mb-2">
{existingIntegration.slug}
</Paragraph>
</InputGroup>
<InputGroup fullWidth>
<Label variant="large">Name</Label>
<Input
type="text"
fullWidth
{...conform.input(title)}
defaultValue={existingIntegration.title ?? undefined}
placeholder={`e.g. Personal ${integration.name}`}
/>
<FormError>{title.error}</FormError>
</InputGroup>
<input type="hidden" name="clientType" value={clientType} />
<div>
<Header2>Use my OAuth App</Header2>
<Paragraph variant="small" className="mb-2">
To use your own OAuth app, check the option below and insert the details.
</Paragraph>
<Checkbox
id="hasCustomClient"
label="Use my OAuth App"
variant="simple/small"
disabled={requiresCustomOAuthApp}
onChange={(checked) => setUseMyOAuthApp(checked)}
{...conform.input(hasCustomClient, { type: "checkbox" })}
defaultChecked={requiresCustomOAuthApp}
/>
{useMyOAuthApp && (
<div className="ml-6 mt-2">
<Paragraph variant="small" className="mb-2">
Set the callback url to <CodeBlock code={callbackUrl} showLineNumbers={false} />
</Paragraph>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<InputGroup fullWidth>
<Label variant="small">Client ID</Label>
<Input fullWidth {...conform.input(customClientId, { type: "text" })} />
</InputGroup>
<InputGroup fullWidth>
<Label variant="small">Client secret</Label>
<Input
fullWidth
{...conform.input(customClientSecret, {
type: "password",
})}
/>
</InputGroup>
</div>
<FormError>{customClientId.error}</FormError>
</div>
</div>
)}
</div>
{authMethod.scopes.length > 0 && (
<div>
<Header2>Scopes</Header2>
<Paragraph variant="small" className="mb-4">
Select the scopes you want to grant to {integration.name} in order for it to access
your data. Note: If you try and perform an action in a Job that requires a scope you
havent granted, that task will fail.
</Paragraph>
{/* <Header3 className="mb-2">
Select from popular scope collections
</Header3>
<fieldset>
<Checkbox
id="allScopes"
label="Select all"
variant="button/small"
/>
</fieldset> */}
<div className="mb-2 mt-4 flex items-center justify-between">
<Header3>Select {integration.name} scopes</Header3>
<Paragraph variant="small" className="text-slate-500">
{simplur`${selectedScopes.size} scope[|s] selected`}
</Paragraph>
</div>
<Input
placeholder="Search scopes"
className="mb-2"
variant="medium"
icon="search"
fullWidth={true}
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
/>
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
{filteredItems.length === 0 && (
<Paragraph variant="small" className="p-4">
No scopes match {filterText}. Try a different search query.
</Paragraph>
)}
{authMethod.scopes.map((s) => {
return (
<Checkbox
key={s.name}
id={s.name}
value={s.name}
name="scopes"
label={s.name}
defaultChecked={s.defaultChecked ?? false}
badges={s.annotations?.map((a) => a.label)}
description={s.description}
variant="description"
className={cn(filteredItems.find((f) => f.name === s.name) ? "" : "hidden")}
onChange={(isChecked) => {
if (isChecked) {
setSelectedScopes((selected) => {
selected.add(s.name);
return new Set(selected);
});
} else {
setSelectedScopes((selected) => {
selected.delete(s.name);
return new Set(selected);
});
}
}}
/>
);
})}
</div>
</div>
)}
</Fieldset>
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
<FormError>{scopes.error}</FormError>
<Button
type="submit"
className="flex gap-2"
disabled={transition.state !== "idle"}
variant="primary/medium"
LeadingIcon={integration.identifier}
>
Connect to {integration.name}
</Button>
</div>
</fetcher.Form>
);
}
+31 -29
View File
@@ -15,17 +15,13 @@ import {
} from "../primitives/Table";
import { SimpleTooltip } from "../primitives/Tooltip";
import { runStatusTitle } from "../runs/RunStatuses";
import { ProjectJob, useProject } from "~/hooks/useProject";
import { ProjectJob } from "~/hooks/useJobs";
import { useProject } from "~/hooks/useProject";
import { useOrganization } from "~/hooks/useOrganizations";
import { JobRunStatus } from "~/models/job.server";
import { cn } from "~/utils/cn";
export function JobsTable({
jobs,
noResultsText,
}: {
jobs: ProjectJob[];
noResultsText: string;
}) {
export function JobsTable({ jobs, noResultsText }: { jobs: ProjectJob[]; noResultsText: string }) {
const organization = useOrganization();
const project = useProject();
@@ -46,7 +42,10 @@ export function JobsTable({
jobs.map((job) => {
const path = jobPath(organization, project, job);
return (
<TableRow key={job.id}>
<TableRow
key={job.id}
className={cn(job.hasIntegrationsRequiringAction && "bg-rose-500/30")}
>
<TableCell to={path}>
<span className="flex items-center gap-2">
<NamedIcon name={job.event.icon} className="h-8 w-8" />
@@ -56,8 +55,7 @@ export function JobsTable({
job.dynamic ? (
<span className="flex items-center gap-0.5">
<NamedIcon name="dynamic" className="h-4 w-4" />{" "}
<span className="uppercase">Dynamic:</span>{" "}
{job.event.title}
<span className="uppercase">Dynamic:</span> {job.event.title}
</span>
) : (
job.event.title
@@ -68,23 +66,31 @@ export function JobsTable({
</span>
</TableCell>
<TableCell to={path}>
<LabelValueStack
label={job.slug}
value={`v${job.version}`}
variant="primary"
/>
<LabelValueStack label={job.slug} value={`v${job.version}`} variant="primary" />
</TableCell>
<TableCell to={path}>
{job.integrations.map((integration) => (
<SimpleTooltip
key={integration.key}
button={
<NamedIcon
name={integration.icon}
className="h-6 w-6"
/>
<div className="relative">
<NamedIcon name={integration.icon} className="h-6 w-6" />
{integration.setupStatus === "MISSING_FIELDS" && (
<NamedIcon name="error" className="absolute -left-1 -top-1 h-4 w-4" />
)}
</div>
}
content={
<div>
<p className="mb-1 text-rose-400">
{integration.setupStatus === "MISSING_FIELDS" &&
"This integration requires configuration"}
</p>
<p>
{integration.title}: {integration.key}
</p>
</div>
}
content={`${integration.title}: ${integration.key}`}
/>
))}
</TableCell>
@@ -126,10 +132,9 @@ export function JobsTable({
</span>
}
value={
<DateTime
date={job.lastRun.createdAt}
className={classForJobStatus(job.lastRun.status)}
/>
<span className={classForJobStatus(job.lastRun.status)}>
<DateTime date={job.lastRun.createdAt} />
</span>
}
/>
) : (
@@ -142,10 +147,7 @@ export function JobsTable({
})
) : (
<TableBlankRow colSpan={6}>
<Paragraph
variant="small"
className="flex items-center justify-center"
>
<Paragraph variant="small" className="flex items-center justify-center">
{noResultsText}
</Paragraph>
</TableBlankRow>
@@ -11,9 +11,7 @@ export function AppContainer({
}) {
return (
<BackgroundGradient showBackgroundGradient={showBackgroundGradient}>
<div className={cn("grid h-full w-full grid-rows-[2.75rem_auto]")}>
{children}
</div>
<div className={cn("grid h-full w-full grid-rows-[2.75rem_auto]")}>{children}</div>
</BackgroundGradient>
);
}
@@ -43,9 +41,7 @@ export function BackgroundGradient({
/** This container should be placed around the content on a page */
export function PageContainer({ children }: { children: React.ReactNode }) {
return (
<div className={cn("grid h-full grid-rows-[auto_1fr]")}>{children}</div>
);
return <div className={cn("grid h-full grid-rows-[auto_1fr]")}>{children}</div>;
}
export function PageBody({
@@ -56,11 +52,7 @@ export function PageBody({
scrollable?: boolean;
}) {
return (
<div
className={cn(scrollable ? "overflow-y-auto p-4 " : "overflow-hidden")}
>
{children}
</div>
<div className={cn(scrollable ? "overflow-y-auto p-4 " : "overflow-hidden")}>{children}</div>
);
}
@@ -68,11 +60,7 @@ export function PageBodyPadding({ children }: { children: React.ReactNode }) {
return <div className="p-4">{children}</div>;
}
export function MainCenteredContainer({
children,
}: {
children: React.ReactNode;
}) {
export function MainCenteredContainer({ children }: { children: React.ReactNode }) {
return (
<div className="h-full w-full">
<div className="mx-auto mt-[30vh] max-w-xs">{children}</div>
@@ -1,211 +1,26 @@
import { useMatches } from "@remix-run/react";
import { Fragment } from "react";
import {
useIntegrationClient,
useOptionalIntegrationClient,
} from "~/hooks/useIntegrationClient";
import { useJob, useOptionalJob } from "~/hooks/useJob";
import {
useOptionalOrganization,
useOrganization,
} from "~/hooks/useOrganizations";
import { useOptionalProject, useProject } from "~/hooks/useProject";
import { useOptionalRun, useRun } from "~/hooks/useRun";
import {
integrationClientConnectionsPath,
integrationClientPath,
integrationClientScopesPath,
jobPath,
jobTestPath,
projectEnvironmentsPath,
projectIntegrationsPath,
projectPath,
runDashboardPath,
} from "~/utils/pathBuilder";
import { RouteMatch, useMatches } from "@remix-run/react";
import { Fragment, ReactNode } from "react";
import { BreadcrumbIcon } from "../primitives/BreadcrumbIcon";
import { JobsMenu } from "./JobsMenu";
import { BreadcrumbLink } from "./NavBar";
import { ProjectsMenu } from "./ProjectsMenu";
export type Breadcrumb = {
slug:
| "projects"
| "jobs"
| "integrations"
| "integration"
| "integration-job"
| "integration-connections"
| "integration-scopes"
| "environments"
| "job"
| "test"
| "runs"
| "run";
link?: {
to: string;
title: string;
};
};
function useBreadcrumbs(): Breadcrumb[] {
const matches = useMatches();
return matches
.filter((match) => match.handle)
.filter((match) => match.handle!.breadcrumb)
.map((match) => match.handle!.breadcrumb as Breadcrumb);
}
export type BreadcrumbItem = (match: RouteMatch, allMatches: RouteMatch[]) => ReactNode;
export function Breadcrumb() {
const breadcrumbs = useBreadcrumbs();
const organization = useOptionalOrganization();
const project = useOptionalProject();
const job = useOptionalJob();
const run = useOptionalRun();
const client = useOptionalIntegrationClient();
const matches = useMatches();
return (
<div className="hidden items-center md:flex">
{breadcrumbs.map((breadcrumb, index) => (
<Fragment key={breadcrumb.slug}>
<BreadcrumbIcon />
<BreadcrumbItem
breadcrumb={breadcrumb}
organization={organization}
project={project}
job={job}
run={run}
client={client}
/>
</Fragment>
))}
{matches.map((match) => {
if (!match.handle || !match.handle.breadcrumb) return null;
const breadcrumb = match.handle.breadcrumb as BreadcrumbItem;
return (
<Fragment key={match.id}>
<BreadcrumbIcon />
{breadcrumb(match, matches)}
</Fragment>
);
})}
</div>
);
}
function BreadcrumbItem({
breadcrumb,
organization,
project,
job,
run,
client,
}: {
breadcrumb: Breadcrumb;
organization?: ReturnType<typeof useOrganization>;
project?: ReturnType<typeof useProject>;
job?: ReturnType<typeof useJob>;
run?: ReturnType<typeof useRun>;
client?: ReturnType<typeof useIntegrationClient>;
}) {
switch (breadcrumb.slug) {
case "projects":
return <ProjectsMenu key={breadcrumb.slug} />;
case "jobs":
return (
<BreadcrumbLink
key={breadcrumb.slug}
to={projectPath(organization!, project!)}
title="Jobs"
/>
);
case "environments":
return (
<BreadcrumbLink
key={breadcrumb.slug}
to={projectEnvironmentsPath(organization!, project!)}
title="Environments"
/>
);
case "integrations":
return (
<BreadcrumbLink
key={breadcrumb.slug}
to={projectIntegrationsPath(organization!, project!)}
title="Integrations"
/>
);
case "integration":
return (
<Fragment key={breadcrumb.slug}>
<BreadcrumbLink
to={projectIntegrationsPath(organization!, project!)}
title="Integrations"
/>
<BreadcrumbIcon />
<BreadcrumbLink
to={integrationClientPath(organization!, project!, client!)}
title={client!.title}
/>
</Fragment>
);
case "integration-job":
return (
<BreadcrumbLink
to={integrationClientPath(organization!, project!, client!)}
title={"Jobs"}
/>
);
case "integration-connections":
return (
<BreadcrumbLink
to={integrationClientConnectionsPath(
organization!,
project!,
client!
)}
title={"Connections"}
/>
);
case "integration-scopes":
return (
<BreadcrumbLink
to={integrationClientScopesPath(organization!, project!, client!)}
title={"Scopes"}
/>
);
case "job":
return (
<Fragment key={breadcrumb.slug}>
<BreadcrumbLink
to={projectPath(organization!, project!)}
title="Jobs"
/>
<BreadcrumbIcon />
<JobsMenu key={breadcrumb.slug} />
</Fragment>
);
case "test":
return (
<BreadcrumbLink
key={breadcrumb.slug}
to={jobTestPath(organization!, project!, job!)}
title="Test"
/>
);
case "runs":
return (
<BreadcrumbLink
key={breadcrumb.slug}
to={jobPath(organization!, project!, job!)}
title="Runs"
/>
);
case "run":
return (
<Fragment key={breadcrumb.slug}>
<BreadcrumbLink
to={jobPath(organization!, project!, job!)}
title="Runs"
/>
<BreadcrumbIcon />
<BreadcrumbLink
to={runDashboardPath(organization!, project!, job!, run!)}
title={`Run #${run!.number}`}
/>
</Fragment>
);
}
return <span className="text-red-500">{breadcrumb.slug}</span>;
}
@@ -1,26 +1,26 @@
import { Link, RouteMatch } from "@remix-run/react";
import { useState } from "react";
import { useJob } from "~/hooks/useJob";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { useJobs } from "~/hooks/useJobs";
import { cn } from "~/utils/cn";
import { jobPath } from "~/utils/pathBuilder";
import { IconNames, NamedIcon } from "../primitives/NamedIcon";
import { LabelValueStack } from "../primitives/LabelValueStack";
import { NamedIcon } from "../primitives/NamedIcon";
import {
Popover,
PopoverArrowTrigger,
PopoverContent,
PopoverMenuItem,
PopoverSectionHeader,
} from "../primitives/Popover";
import { LabelValueStack } from "../primitives/LabelValueStack";
import { LinkButton } from "../primitives/Buttons";
import { Link } from "@remix-run/react";
import { cn } from "~/utils/cn";
export function JobsMenu() {
export function JobsMenu({ matches }: { matches: RouteMatch[] }) {
const [isOpen, setIsOpen] = useState(false);
const organization = useOrganization();
const project = useProject();
const currentJob = useJob();
const organization = useOrganization(matches);
const project = useProject(matches);
const projectJobs = useJobs(matches);
const currentJob = useJob(matches);
return (
<>
@@ -29,33 +29,31 @@ export function JobsMenu() {
{currentJob?.title ?? "Select a job"}
</PopoverArrowTrigger>
<PopoverContent
className="w-80 overflow-y-auto p-0"
className="min-w-[20rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
align="start"
collisionPadding={20}
>
<PopoverSectionHeader title="Jobs" />
<div className="flex flex-col gap-1 p-1">
{project.jobs.map((job) => {
{projectJobs.map((job) => {
const isSelected = job.id === currentJob?.id;
return (
<Link
key={job.id}
to={jobPath(organization, project, job)}
className={cn(
"flex w-full items-center gap-2 rounded-md p-2",
"flex w-full items-center gap-2 rounded-md p-2 transition hover:bg-slate-800",
isSelected && "bg-slate-750 group-hover:bg-slate-750"
)}
>
<NamedIcon name={job.event.icon} className="h-6 w-6" />
<NamedIcon name={job.event.icon} className="h-8 w-8" />
<LabelValueStack
label={job.title}
value={job.slug}
variant="primary"
className="w-full"
/>
{isSelected && (
<NamedIcon name="check" className="mr-1 h-6 w-6" />
)}
{isSelected && <NamedIcon name="check" className="mr-1 h-6 w-6" />}
</Link>
);
})}
@@ -1,17 +1,17 @@
import { Popover, Transition } from "@headlessui/react";
import { BookOpenIcon } from "@heroicons/react/20/solid";
import { BookOpenIcon, ChatBubbleLeftRightIcon } from "@heroicons/react/20/solid";
import { Link } from "@remix-run/react";
import { Fragment } from "react";
import { cn } from "~/utils/cn";
import { Feedback } from "../Feedback";
import { LogoIcon } from "../LogoIcon";
import { LinkButton } from "../primitives/Buttons";
import { Button, LinkButton } from "../primitives/Buttons";
import { Breadcrumb } from "./Breadcrumb";
import { docsRoot } from "~/utils/pathBuilder";
export function NavBar() {
return (
<div className="z-50 flex w-full items-center justify-between gap-2 border-b border-divide py-1 pl-2 pr-2.5">
<div className="z-50 flex w-full items-center justify-between gap-2 border-b border-divide py-1 pl-1 pr-2.5">
<div className="flex gap-0.5">
<Link to="/" className="p-2">
<LogoIcon className="h-5 w-5" />
@@ -20,14 +20,20 @@ export function NavBar() {
<MobileDropdownMenu />
</div>
<div className="hidden items-center gap-2 sm:flex">
<LinkButton
to={docsRoot()}
variant="secondary/small"
LeadingIcon={BookOpenIcon}
>
<LinkButton to={docsRoot()} variant="secondary/small" LeadingIcon={BookOpenIcon}>
Documentation
</LinkButton>
<Feedback />
<Feedback
button={
<Button
variant="secondary/small"
LeadingIcon={ChatBubbleLeftRightIcon}
shortcut={{ key: "f" }}
>
Help & feedback
</Button>
}
/>
</div>
</div>
);
@@ -115,10 +121,7 @@ function MobileNavIcon({ open }: { open: boolean }) {
/>
<path
d="M2 2L12 12M12 2L2 12"
className={cn(
"origin-center transition",
!open && "scale-90 opacity-0"
)}
className={cn("origin-center transition", !open && "scale-90 opacity-0")}
/>
</svg>
);
@@ -1,10 +1,10 @@
import { useMatches } from "@remix-run/react";
import { motion } from "framer-motion";
import invariant from "tiny-invariant";
import { useJob, useOptionalJob } from "~/hooks/useJob";
import { useOptionalJob } from "~/hooks/useJob";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { cn } from "~/utils/cn";
import { Handle } from "~/utils/handle";
import {
accountPath,
organizationBillingPath,
@@ -12,19 +12,15 @@ import {
projectEnvironmentsPath,
projectIntegrationsPath,
projectPath,
projectTriggersPath,
} from "~/utils/pathBuilder";
import { NavLinkButton } from "../primitives/Buttons";
import type { IconNames } from "../primitives/NamedIcon";
import { SimpleTooltip } from "../primitives/Tooltip";
import { UserProfilePhoto } from "../UserProfilePhoto";
import { useOptionalIntegrationClient } from "~/hooks/useIntegrationClient";
import { NavLinkButton } from "../primitives/Buttons";
import { NamedIcon, type IconNames } from "../primitives/NamedIcon";
import { SimpleTooltip } from "../primitives/Tooltip";
export function SideMenuContainer({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-full w-full justify-stretch overflow-hidden">
{children}
</div>
);
return <div className="flex h-full w-full justify-stretch overflow-hidden">{children}</div>;
}
const expandedWith = "14rem";
@@ -46,15 +42,15 @@ export function ProjectSideMenu() {
const project = useProject();
const matches = useMatches();
//we collapse the menu if we're in a job or an integration
const job = useOptionalJob();
const client = useOptionalIntegrationClient();
const isCollapsed = job !== undefined || client !== undefined;
//the deepest route `handle` determines if the menu is expanded
const deepestMatch = matches.at(-1);
const handle = deepestMatch?.handle as Handle;
const isCollapsed = handle?.expandSidebar ? !handle.expandSidebar : true;
const job = useOptionalJob();
const jobsActive =
job !== undefined ||
matches.at(-1)?.id ===
"routes/_app.orgs.$organizationSlug.projects.$projectParam._index";
deepestMatch?.id === "routes/_app.orgs.$organizationSlug.projects.$projectParam._index";
return (
<motion.div
@@ -72,18 +68,30 @@ export function ProjectSideMenu() {
to={projectPath(organization, project)}
isCollapsed={isCollapsed}
forceActive={jobsActive}
data-action="jobs"
/>
<SideMenuItem
name="Integrations"
icon="integration"
to={projectIntegrationsPath(organization, project)}
isCollapsed={isCollapsed}
hasWarning={project.hasUnconfiguredIntegrations}
data-action="integrations"
/>
<SideMenuItem
name="Triggers"
icon="trigger"
to={projectTriggersPath(organization, project)}
isCollapsed={isCollapsed}
hasWarning={project.hasInactiveExternalTriggers}
data-action="triggers"
/>
<SideMenuItem
name="Environments & API Keys"
icon="environment"
to={projectEnvironmentsPath(organization, project)}
isCollapsed={isCollapsed}
data-action="environments & api keys"
/>
</div>
<div className="flex flex-col">
@@ -92,18 +100,21 @@ export function ProjectSideMenu() {
icon="team"
to={organizationTeamPath(organization)}
isCollapsed={isCollapsed}
data-action="team"
/>
<SideMenuItem
name="Usage & Billing"
icon="billing"
to={organizationBillingPath(organization)}
isCollapsed={isCollapsed}
data-action="usage & billing"
/>
<SideMenuItem
name="Account"
icon={UserProfilePhoto}
to={accountPath()}
isCollapsed={isCollapsed}
data-action="account"
/>
</div>
</motion.div>
@@ -125,11 +136,13 @@ function SideMenuItem({
to,
isCollapsed,
forceActive,
hasWarning = false,
}: {
icon: IconNames | React.ComponentType<any>;
name: string;
to: string;
isCollapsed: boolean;
hasWarning?: boolean;
forceActive?: boolean;
}) {
return (
@@ -147,6 +160,7 @@ function SideMenuItem({
isActive = forceActive;
}
return cn(
"relative",
isActive
? "bg-slate-800 text-bright group-hover:bg-slate-800"
: "text-dimmed group-hover:bg-slate-850 group-hover:text-bright"
@@ -161,6 +175,7 @@ function SideMenuItem({
>
{name}
</motion.span>
{hasWarning && <NamedIcon name="error" className="absolute left-1 top-1 h-4 w-4" />}
</NavLinkButton>
}
content={name}
@@ -1,14 +1,10 @@
import { RouteMatch } from "@remix-run/react";
import { Fragment, useState } from "react";
import {
useIsNewOrganizationPage,
useOrganizations,
} from "~/hooks/useOrganizations";
import { useOptionalProject, useProject } from "~/hooks/useProject";
import {
newOrganizationPath,
newProjectPath,
projectPath,
} from "~/utils/pathBuilder";
import simplur from "simplur";
import { Badge } from "~/components/primitives/Badge";
import { useIsNewOrganizationPage, useOrganizations } from "~/hooks/useOrganizations";
import { useOptionalProject } from "~/hooks/useProject";
import { newOrganizationPath, newProjectPath, projectPath } from "~/utils/pathBuilder";
import {
Popover,
PopoverArrowTrigger,
@@ -17,11 +13,11 @@ import {
PopoverSectionHeader,
} from "../primitives/Popover";
export function ProjectsMenu() {
export function ProjectsMenu({ matches }: { matches: RouteMatch[] }) {
const [isOpen, setIsOpen] = useState(false);
const organizations = useOrganizations();
const isNewOrgPage = useIsNewOrganizationPage();
const currentProject = useOptionalProject();
const organizations = useOrganizations(matches);
const isNewOrgPage = useIsNewOrganizationPage(matches);
const currentProject = useOptionalProject(matches);
if (isNewOrgPage) {
return null;
@@ -33,18 +29,27 @@ export function ProjectsMenu() {
<PopoverArrowTrigger isOpen={isOpen}>
{currentProject?.name ?? "Select a project"}
</PopoverArrowTrigger>
<PopoverContent className="w-80 p-0" align="start">
<PopoverContent
className="min-w-[20rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
align="start"
collisionPadding={20}
>
{organizations.map((organization) => (
<Fragment key={organization.id}>
<PopoverSectionHeader title={organization.title} />
<div className="flex flex-col gap-1 p-1">
<div className="flex flex-col gap-1 p-1 ">
{organization.projects.map((project) => {
const isSelected = project.id === currentProject?.id;
return (
<PopoverMenuItem
key={project.id}
to={projectPath(organization, project)}
title={project.name}
title={
<div className="flex w-full items-center justify-between pl-1 text-bright">
<span className="grow truncate text-left">{project.name}</span>
<Badge className="mr-0.5">{simplur`${project._count.jobs} job[|s]`}</Badge>
</div>
}
isSelected={isSelected}
icon="folder"
/>
@@ -54,29 +54,14 @@ const AlertContent = React.forwardRef<
));
AlertContent.displayName = AlertDialogPrimitive.Content.displayName;
const AlertHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
const AlertHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
);
AlertHeader.displayName = "AlertDialogHeader";
const AlertFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
const AlertFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props}
/>
);
@@ -118,11 +103,7 @@ const AlertCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn("mt-2 sm:mt-0", className)}
{...props}
/>
<AlertDialogPrimitive.Cancel ref={ref} className={cn("mt-2 sm:mt-0", className)} {...props} />
));
AlertCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
@@ -4,16 +4,14 @@ import { cn } from "~/utils/cn";
const variants = {
default:
"grid place-items-center rounded-full px-2 h-5 tracking-wider text-xxs bg-slate-700 text-bright uppercase whitespace-nowrap",
outline:
"grid place-items-center rounded-sm px-1 h-5 tracking-wider text-xxs border border-dimmed text-dimmed uppercase whitespace-nowrap",
};
type BadgeProps = React.HTMLAttributes<HTMLDivElement> & {
variant?: keyof typeof variants;
};
export function Badge({
className,
variant = "default",
...props
}: BadgeProps) {
export function Badge({ className, variant = "default", ...props }: BadgeProps) {
return <div className={cn(variants[variant], className)} {...props} />;
}
@@ -1,19 +1,13 @@
import { Link, LinkProps, NavLink, NavLinkProps } from "@remix-run/react";
import React, {
ReactComponentElement,
forwardRef,
useImperativeHandle,
useRef,
} from "react";
import React, { ReactComponentElement, forwardRef, useImperativeHandle, useRef } from "react";
import { ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys";
import { cn } from "~/utils/cn";
import { NamedIcon } from "./NamedIcon";
import { IconNamesOrString, NamedIcon } from "./NamedIcon";
import { ShortcutKey } from "./ShortcutKey";
const variant = {
"primary/small": {
textColor:
"text-bright group-hover:text-white transition group-disabled:text-bright/80",
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80",
button:
"h-6 px-[5px] text-xs bg-indigo-600 group-hover:bg-indigo-500/90 group-disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
@@ -22,8 +16,7 @@ const variant = {
"ml-1 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60 justify-self-center",
},
"secondary/small": {
textColor:
"text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button:
"h-6 px-[5px] text-xs bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
@@ -32,8 +25,7 @@ const variant = {
"ml-1 -mr-0.5 border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60",
},
"tertiary/small": {
textColor:
"text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button:
"h-6 px-[5px] text-xs bg-transparent group-hover:bg-slate-850 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
@@ -42,58 +34,45 @@ const variant = {
"ml-1 -mr-0.5 border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60",
},
"danger/small": {
textColor:
"text-bright group-hover:text-white transition group-disabled:text-bright/80",
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80",
button:
"h-6 px-[5px] text-xs bg-rose-600 group-hover:bg-rose-500 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
shortcutVariant: "small" as const,
shortcut:
"ml-1 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
shortcut: "ml-1 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/medium": {
textColor:
"text-bright group-hover:text-white transition group-disabled:text-bright/80",
button:
"h-8 px-2 text-sm bg-indigo-600 group-hover:bg-indigo-500/90 disabled:opacity-50",
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80",
button: "h-8 px-2 text-sm bg-indigo-600 group-hover:bg-indigo-500/90 disabled:opacity-50",
icon: "h-4",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"secondary/medium": {
textColor:
"text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button:
"h-8 px-2 text-sm bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50",
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button: "h-8 px-2 text-sm bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50",
icon: "h-4",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-dimmed/40 text-dimmed group-hover:border-dimmed group-hover:text-bright",
},
"tertiary/medium": {
textColor:
"text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button:
"h-8 px-2 text-sm bg-transparent group-hover:bg-slate-850 disabled:opacity-50",
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80",
button: "h-8 px-2 text-sm bg-transparent group-hover:bg-slate-850 disabled:opacity-50",
icon: "h-4",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-bright/40 text-dimmed group-hover:border-bright/60 group-hover:text-bright",
},
"danger/medium": {
textColor:
"text-bright group-hover:text-white transition group-disabled:text-bright/80",
button:
"h-8 px-2 text-sm bg-rose-600 group-hover:bg-rose-500 disabled:opacity-50",
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80",
button: "h-8 px-2 text-sm bg-rose-600 group-hover:bg-rose-500 disabled:opacity-50",
icon: "h-4",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/large": {
textColor:
"text-bright group-hover:text-white transition group-disabled:text-dimmed/80",
textColor: "text-bright group-hover:text-white transition group-disabled:text-dimmed/80",
button:
"h-10 px-2 text-sm font-medium bg-indigo-600 group-hover:bg-indigo-500/90 disabled:opacity-50",
icon: "h-5",
@@ -125,8 +104,8 @@ const allVariants = {
type ButtonContentPropsType = {
children?: React.ReactNode;
LeadingIcon?: React.ComponentType<any> | string;
TrailingIcon?: React.ComponentType<any> | string;
LeadingIcon?: React.ComponentType<any> | IconNamesOrString;
TrailingIcon?: React.ComponentType<any> | IconNamesOrString;
trailingIconClassName?: string;
leadingIconClassName?: string;
fullWidth?: boolean;
@@ -157,13 +136,7 @@ export function ButtonContent(props: ButtonContentPropsType) {
const textColorClassName = variation.textColor;
return (
<div
className={cn(
fullWidth ? "flex" : "inline-flex text-xxs",
btnClassName,
className
)}
>
<div className={cn(fullWidth ? "flex" : "inline-flex text-xxs", btnClassName, className)}>
<div
className={cn(
textAlignLeft ? "text-left" : "justify-center",
@@ -174,11 +147,7 @@ export function ButtonContent(props: ButtonContentPropsType) {
(typeof LeadingIcon === "string" ? (
<NamedIcon
name={LeadingIcon}
className={cn(
iconClassName,
leadingIconClassName,
"shrink-0 justify-start"
)}
className={cn(iconClassName, leadingIconClassName, "shrink-0 justify-start")}
/>
) : (
<LeadingIcon
@@ -193,12 +162,7 @@ export function ButtonContent(props: ButtonContentPropsType) {
{text &&
(typeof text === "string" ? (
<span
className={cn(
"mx-auto grow self-center truncate px-1",
textColorClassName
)}
>
<span className={cn("mx-auto grow self-center truncate px-1", textColorClassName)}>
{text}
</span>
) : (
@@ -209,11 +173,7 @@ export function ButtonContent(props: ButtonContentPropsType) {
(typeof TrailingIcon === "string" ? (
<NamedIcon
name={TrailingIcon}
className={cn(
iconClassName,
trailingIconClassName,
"shrink-0 justify-end"
)}
className={cn(iconClassName, trailingIconClassName, "shrink-0 justify-end")}
/>
) : (
<TrailingIcon
@@ -277,8 +237,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
}
);
type LinkPropsType = Pick<LinkProps, "to" | "target"> &
React.ComponentProps<typeof ButtonContent>;
type LinkPropsType = Pick<LinkProps, "to" | "target"> & React.ComponentProps<typeof ButtonContent>;
export const LinkButton = ({ to, ...props }: LinkPropsType) => {
const innerRef = useRef<HTMLAnchorElement>(null);
if (props.shortcut) {
@@ -317,26 +276,13 @@ export const LinkButton = ({ to, ...props }: LinkPropsType) => {
type NavLinkPropsType = Pick<NavLinkProps, "to" | "target"> &
Omit<React.ComponentProps<typeof ButtonContent>, "className"> & {
className?: (props: {
isActive: boolean;
isPending: boolean;
}) => string | undefined;
className?: (props: { isActive: boolean; isPending: boolean }) => string | undefined;
};
export const NavLinkButton = ({
to,
className,
...props
}: NavLinkPropsType) => {
export const NavLinkButton = ({ to, className, ...props }: NavLinkPropsType) => {
return (
<NavLink
to={to}
className={cn("group outline-none", props.fullWidth ? "w-full" : "")}
>
<NavLink to={to} className={cn("group outline-none", props.fullWidth ? "w-full" : "")}>
{({ isActive, isPending }) => (
<ButtonContent
className={className && className({ isActive, isPending })}
{...props}
/>
<ButtonContent className={className && className({ isActive, isPending })} {...props} />
)}
</NavLink>
);
@@ -4,49 +4,52 @@ import {
InformationCircleIcon,
LightBulbIcon,
} from "@heroicons/react/20/solid";
import { cn } from "~/utils/cn";
import { Paragraph } from "./Paragraph";
import {
ArrowTopRightOnSquareIcon,
BookOpenIcon,
CheckCircleIcon,
ChevronRightIcon,
} from "@heroicons/react/24/solid";
import { Link } from "@remix-run/react";
import { cn } from "~/utils/cn";
import { Paragraph } from "./Paragraph";
const variantClasses = {
export const variantClasses = {
info: {
className: "border-blue-400/20 bg-blue-400/30",
icon: <InformationCircleIcon className="h-5 w-5 shrink-0 text-blue-400" />,
text: "text-blue-200",
textColor: "text-blue-200",
linkClassName: "transition hover:bg-blue-400/40",
},
warning: {
className: "border-yellow-400/20 bg-yellow-400/30",
icon: (
<ExclamationCircleIcon className="h-5 w-5 shrink-0 text-yellow-400" />
),
text: "text-yellow-200",
icon: <ExclamationTriangleIcon className="h-5 w-5 shrink-0 text-yellow-400" />,
textColor: "text-yellow-200",
linkClassName: "transition hover:bg-yellow-400/40",
},
error: {
className: "border-[hsl(347,77%,50%)]/30 bg-[hsl(347,77%,50%)]/20",
icon: (
<ExclamationTriangleIcon className="h-5 w-5 shrink-0 text-rose-400" />
),
text: "text-rose-200",
className: "border-rose-500/20 bg-rose-500/30",
icon: <ExclamationCircleIcon className="h-5 w-5 shrink-0 text-rose-400" />,
textColor: "text-rose-200",
linkClassName: "transition hover:bg-rose-500/40",
},
idea: {
className: "border-green-400/20 bg-green-400/30",
icon: <LightBulbIcon className="h-5 w-5 shrink-0 text-green-400" />,
text: "text-green-200",
textColor: "text-green-200",
linkClassName: "transition hover:bg-green-400/40",
},
success: {
className: "border-green-400/20 bg-green-400/30",
icon: <CheckCircleIcon className="h-5 w-5 shrink-0 text-green-400" />,
text: "text-green-200",
textColor: "text-green-200",
linkClassName: "transition hover:bg-green-400/40",
},
docs: {
className:
"border-blue-400/20 bg-blue-400/30 transition hover:bg-blue-400/40",
icon: <BookOpenIcon className="h-5 w-5 shrink-0 text-blue-400" />,
text: "text-blue-200",
className: "border-blue-400/20 bg-blue-400/30",
icon: <BookOpenIcon className="mt-0.5 h-5 w-5 shrink-0 text-blue-400" />,
textColor: "text-blue-200",
linkClassName: "transition hover:bg-blue-400/40",
},
} as const;
@@ -55,41 +58,69 @@ export function Callout({
className,
icon,
variant,
href,
to,
}: {
children?: React.ReactNode;
className?: string;
icon?: React.ReactNode;
variant: keyof typeof variantClasses;
href?: string;
to?: string;
}) {
const variantDefinition = variantClasses[variant];
if (variant === "docs") {
return (
<a
href={href}
target="_blank"
className={cn(
`flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`,
variantDefinition.className,
className
)}
>
<div className="flex w-full items-center gap-x-2">
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
<Paragraph variant={"small"} className={variantDefinition.text}>
{children}
</Paragraph>
) : (
children
if (to !== undefined) {
if (to.startsWith("http")) {
return (
<a
href={to}
target="_blank"
className={cn(
`flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`,
variantDefinition.className,
variantDefinition.linkClassName,
className
)}
</div>
<ArrowTopRightOnSquareIcon className="h-5 w-5 text-blue-400" />
</a>
);
>
<div className={"flex w-full items-start gap-x-2"}>
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
<Paragraph variant={"small"} className={variantDefinition.textColor}>
{children}
</Paragraph>
) : (
children
)}
</div>
<ArrowTopRightOnSquareIcon className={cn("h-5 w-5", variantDefinition.textColor)} />
</a>
);
} else {
return (
<Link
to={to}
className={cn(
`flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`,
variantDefinition.className,
variantDefinition.linkClassName,
className
)}
>
<div className={"flex w-full items-start gap-x-2"}>
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
<Paragraph variant={"small"} className={variantDefinition.textColor}>
{children}
</Paragraph>
) : (
children
)}
</div>
<ChevronRightIcon className={cn("h-5 w-5", variantDefinition.textColor)} />
</Link>
);
}
}
return (
@@ -103,7 +134,7 @@ export function Callout({
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
<Paragraph variant={"small"} className={variantDefinition.text}>
<Paragraph variant={"small"} className={variantDefinition.textColor}>
{children}
</Paragraph>
) : (
@@ -82,9 +82,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
},
ref
) => {
const [isChecked, setIsChecked] = useState<boolean>(
defaultChecked ?? false
);
const [isChecked, setIsChecked] = useState<boolean>(defaultChecked ?? false);
const [isDisabled, setIsDisabled] = useState<boolean>(disabled ?? false);
const buttonClassName = variants[variant].button;
@@ -157,10 +155,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
)}
</div>
{variant === "description" && (
<Paragraph
variant="small"
className={cn("mt-0.5", descriptionClassName)}
>
<Paragraph variant="small" className={cn("mt-0.5", descriptionClassName)}>
{description}
</Paragraph>
)}
@@ -12,10 +12,7 @@ const ClientTabsList = React.forwardRef<
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex items-center justify-center transition duration-100",
className
)}
className={cn("inline-flex items-center justify-center transition duration-100", className)}
{...props}
/>
));
@@ -106,31 +106,19 @@ export function ClipboardField({
const inputIcon = useRef<HTMLInputElement>(null);
return (
<div
className={cn(container, fullWidth ? "w-full" : "max-w-max", className)}
>
<span className={cn(container, fullWidth ? "w-full" : "max-w-max", className)}>
{icon && (
<div
<span
onClick={() => inputIcon.current && inputIcon.current.focus()}
className={cn(iconPosition, "flex items-center")}
>
{typeof icon === "string" ? (
<NamedIcon name={icon} className={iconClassName} />
) : (
icon
)}
</div>
{typeof icon === "string" ? <NamedIcon name={icon} className={iconClassName} /> : icon}
</span>
)}
<input
type="text"
ref={inputIcon}
value={
isSecure
? typeof secure === "string"
? secure
: "•".repeat(value.length)
: value
}
value={isSecure ? (typeof secure === "string" ? secure : "•".repeat(value.length)) : value}
readOnly={true}
className={cn("shrink grow select-all overflow-x-auto", input)}
size={value.length}
@@ -146,13 +134,9 @@ export function ClipboardField({
}
}}
/>
<Button
variant={buttonVariant}
onClick={copy}
className={cn("shrink-0 grow-0", button)}
>
<Button variant={buttonVariant} onClick={copy} className={cn("shrink-0 grow-0", button)}>
{copied ? <CheckIcon className="h-4 w-4 text-green-500" /> : "Copy"}
</Button>
</div>
</span>
);
}
@@ -1,60 +1,78 @@
import { Fragment, useEffect, useState } from "react";
import { useLocales } from "./LocaleProvider";
type DateTimeProps = {
date: Date | string;
timeZone?: string;
className?: string;
includeSeconds?: boolean;
};
export const DateTime = ({ date, timeZone, className }: DateTimeProps) => {
export const DateTime = ({ date, timeZone = "UTC", includeSeconds = true }: DateTimeProps) => {
const locales = useLocales();
const realDate = typeof date === "string" ? new Date(date) : date;
const locales = useLocales();
const isoString = realDate.toISOString();
const formattedDate = new Intl.DateTimeFormat(locales, {
const initialFormattedDateTime = formatDateTime(realDate, timeZone, locales, includeSeconds);
const [formattedDateTime, setFormattedDateTime] = useState<string>(initialFormattedDateTime);
useEffect(() => {
const resolvedOptions = Intl.DateTimeFormat().resolvedOptions();
setFormattedDateTime(
formatDateTime(realDate, resolvedOptions.timeZone, locales, includeSeconds)
);
}, [locales, includeSeconds, realDate]);
return <Fragment>{formattedDateTime.replace(/\s/g, String.fromCharCode(32))}</Fragment>;
};
function formatDateTime(
date: Date,
timeZone: string,
locales: string[],
includeSeconds: boolean
): string {
return new Intl.DateTimeFormat(locales, {
year: "numeric",
month: "short",
day: "numeric",
timeZone,
}).format(realDate);
const formattedTime = new Intl.DateTimeFormat(locales, {
hour: "numeric",
minute: "numeric",
second: includeSeconds ? "numeric" : undefined,
timeZone,
}).format(realDate);
}).format(date);
}
return (
<time dateTime={isoString} className={className}>
{formattedDate} at {formattedTime}
</time>
);
};
export const DateTimeAccurate = ({ date, timeZone = "UTC" }: DateTimeProps) => {
const locales = useLocales();
export const DateTimeAccurate = ({ date, timeZone }: DateTimeProps) => {
const realDate = typeof date === "string" ? new Date(date) : date;
const locales = useLocales();
const isoString = realDate.toISOString();
const formattedDate = new Intl.DateTimeFormat(locales, {
const initialFormattedDateTime = formatDateTimeAccurate(realDate, timeZone, locales);
const [formattedDateTime, setFormattedDateTime] = useState<string>(initialFormattedDateTime);
useEffect(() => {
const resolvedOptions = Intl.DateTimeFormat().resolvedOptions();
setFormattedDateTime(formatDateTimeAccurate(realDate, resolvedOptions.timeZone, locales));
}, [locales, realDate]);
return <Fragment>{formattedDateTime.replace(/\s/g, String.fromCharCode(32))}</Fragment>;
};
function formatDateTimeAccurate(date: Date, timeZone: string, locales: string[]): string {
const milliseconds = `00${date.getMilliseconds()}`.slice(-3);
const formattedDateTime = new Intl.DateTimeFormat(locales, {
month: "short",
day: "2-digit",
timeZone,
}).format(realDate);
const formattedTime = new Intl.DateTimeFormat(locales, {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: false,
timeZone,
}).format(realDate);
}).format(date);
const milliseconds = `00${realDate.getMilliseconds()}`.slice(-3);
return (
<time dateTime={isoString}>
{formattedDate} {formattedTime}.{milliseconds}
</time>
);
};
return `${formatDateTime}.${milliseconds}`;
}
@@ -9,11 +9,7 @@ const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = ({
className,
children,
...props
}: DialogPrimitive.DialogPortalProps) => (
const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => (
<DialogPrimitive.Portal className={cn(className)} {...props}>
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
{children}
@@ -61,29 +57,14 @@ const DialogContent = React.forwardRef<
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props}
/>
);
@@ -95,10 +76,7 @@ const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
));
@@ -7,7 +7,5 @@ export function Fieldset({
children: React.ReactNode;
className?: string;
}) {
return (
<div className={cn("flex flex-col gap-y-5", className)}>{children}</div>
);
return <div className={cn("flex flex-col gap-y-5", className)}>{children}</div>;
}
@@ -3,13 +3,7 @@ import { Paragraph } from "./Paragraph";
import { NamedIcon } from "./NamedIcon";
import { motion } from "framer-motion";
export function FormError({
children,
id,
}: {
children: React.ReactNode;
id?: string;
}) {
export function FormError({ children, id }: { children: React.ReactNode; id?: string }) {
return (
<>
{children && (
@@ -29,13 +23,7 @@ export function FormError({
);
}
export function ZodFormErrors({
errors,
path,
}: {
errors: z.ZodIssue[];
path: string[];
}) {
export function ZodFormErrors({ errors, path }: { errors: z.ZodIssue[]; path: string[] }) {
if (errors.length === 0) {
return null;
}
@@ -54,10 +54,7 @@ export default function FormSegmentedControl({
<div className="text-sm">
<RadioGroup.Label
as="p"
className={cn(
"font-normal",
checked ? "text-bright" : "text-dimmed"
)}
className={cn("font-normal", checked ? "text-bright" : "text-dimmed")}
>
{option.label}
</RadioGroup.Label>
@@ -1,40 +1,90 @@
import { cn } from "~/utils/cn";
const header1Styles =
"font-sans text-base md:text-lg lg:text-xl leading-5 md:leading-6 lg:leading-7 font-semibold";
const header2Styles = "font-sans text-base leading-6 font-medium text-bright";
const header3Styles = "font-sans text-sm leading-5 font-medium text-bright";
const headerVariants = {
header1: {
text: "font-sans text-base md:text-lg lg:text-xl leading-5 md:leading-6 lg:leading-7 font-semibold",
spacing: "mb-2",
},
header2: {
text: "font-sans text-base leading-6 font-medium",
spacing: "mb-2",
},
header3: {
text: "font-sans text-sm leading-5 font-medium",
spacing: "mb-2",
},
};
const header1Variants = {
default: "text-bright",
const textColorVariants = {
bright: "text-bright",
dimmed: "text-dimmed",
};
export type HeaderVariant = keyof typeof headerVariants;
type HeaderProps = {
className?: string;
children: React.ReactNode;
};
type Header1Props = HeaderProps & {
variant?: keyof typeof header1Variants;
};
spacing?: boolean;
textColor?: "bright" | "dimmed";
} & React.HTMLAttributes<HTMLHeadingElement>;
export function Header1({
variant = "default",
className,
children,
}: Header1Props) {
spacing = false,
textColor = "bright",
}: HeaderProps) {
return (
<h1 className={cn(header1Styles, header1Variants[variant], className)}>
<h1
className={cn(
headerVariants.header1.text,
spacing === true && headerVariants.header1.spacing,
textColor === "bright" ? textColorVariants.bright : textColorVariants.dimmed,
className
)}
>
{children}
</h1>
);
}
export function Header2({ className, children }: HeaderProps) {
return <h2 className={cn(header2Styles, className)}>{children}</h2>;
export function Header2({
className,
children,
spacing = false,
textColor = "bright",
}: HeaderProps) {
return (
<h2
className={cn(
headerVariants.header2.text,
spacing === true && headerVariants.header2.spacing,
textColor === "bright" ? textColorVariants.bright : textColorVariants.dimmed,
className
)}
>
{children}
</h2>
);
}
export function Header3({ className, children }: HeaderProps) {
return <h3 className={cn(header3Styles, className)}>{children}</h3>;
export function Header3({
className,
children,
spacing = false,
textColor = "bright",
}: HeaderProps) {
return (
<h3
className={cn(
headerVariants.header3.text,
spacing === true && headerVariants.header3.spacing,
textColor === "bright" ? textColorVariants.bright : textColorVariants.dimmed,
className
)}
>
{children}
</h3>
);
}
@@ -29,11 +29,7 @@ function useHelp() {
return React.useContext(HelpContext);
}
export function Help({
defaultOpen,
allowDismissing = true,
children,
}: HelpProps) {
export function Help({ defaultOpen, allowDismissing = true, children }: HelpProps) {
const [open, setOpen] = React.useState(defaultOpen || false);
return (
@@ -52,7 +48,7 @@ export function HelpTrigger({ title }: { title: string }) {
<Button
variant="tertiary/small"
LeadingIcon="lightbulb"
leadingIconClassName="text-slate-400"
leadingIconClassName="text-amber-400"
onClick={() => setOpen(true)}
>
{title}
@@ -1,6 +1,6 @@
import * as React from "react";
import { cn } from "~/utils/cn";
import type { IconNames } from "./NamedIcon";
import type { IconNamesOrString } from "./NamedIcon";
import { NamedIcon } from "./NamedIcon";
const variants = {
@@ -44,24 +44,13 @@ const variants = {
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
variant?: keyof typeof variants;
icon?: IconNames;
icon?: IconNamesOrString;
shortcut?: string;
fullWidth?: boolean;
};
const Input = React.forwardRef<HTMLInputElement, InputProps>(
(
{
className,
type,
shortcut,
fullWidth = true,
variant = "medium",
icon,
...props
},
ref
) => {
({ className, type, shortcut, fullWidth = true, variant = "medium", icon, ...props }, ref) => {
const inputClassName = variants[variant].input;
const iconClassName = variants[variant].iconSize;
const iconOffsetClassName = variants[variant].iconOffset;
@@ -70,25 +59,16 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<div className={cn("relative", fullWidth ? "w-full" : "max-w-max")}>
{icon && (
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center">
<NamedIcon
name={icon}
className={cn(iconClassName, "text-dimmed")}
/>
<NamedIcon name={icon} className={cn(iconClassName, "text-dimmed")} />
</div>
)}
<input
type={type}
className={cn(
inputClassName,
icon ? iconOffsetClassName : "",
className
)}
className={cn(inputClassName, icon ? iconOffsetClassName : "", className)}
ref={ref}
{...props}
/>
{shortcut && (
<div className={cn(shortcutClassName, "absolute")}>{shortcut}</div>
)}
{shortcut && <div className={cn(shortcutClassName, "absolute")}>{shortcut}</div>}
</div>
);
}
@@ -6,11 +6,7 @@ type InputGroupProps = {
fullWidth?: boolean;
};
export function InputGroup({
children,
className,
fullWidth,
}: InputGroupProps) {
export function InputGroup({ children, className, fullWidth }: InputGroupProps) {
return (
<div
className={cn(
+18 -20
View File
@@ -1,31 +1,29 @@
import * as React from "react";
import { cn } from "~/utils/cn";
import { Header2, Header3 } from "./Headers";
import { Paragraph } from "./Paragraph";
const labelVariants = {
small: {
text: "font-sans text-sm font-normal",
},
medium: {
text: "font-sans text-sm leading-5 font-medium",
},
large: {
text: "font-sans text-base leading-6 font-medium",
},
};
type LabelProps = React.AllHTMLAttributes<HTMLLabelElement> & {
className?: string;
children: React.ReactNode;
variant?: "small" | "medium" | "large";
variant?: keyof typeof labelVariants;
};
export function Label({
className,
children,
variant = "medium",
...props
}: LabelProps) {
return variant === "medium" ? (
<Header3 className={cn(className)} {...props}>
export function Label({ className, children, variant = "medium", ...props }: LabelProps) {
const variation = labelVariants[variant];
return (
<label className={cn(variation.text, className)} {...props}>
{children}
</Header3>
) : variant === "large" ? (
<Header2 className={cn(className)} {...props}>
{children}
</Header2>
) : (
<Paragraph variant="small" className={cn(className)}>
{children}
</Paragraph>
</label>
);
}
@@ -3,10 +3,7 @@ import { Paragraph, ParagraphVariant } from "./Paragraph";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/20/solid";
import { SimpleTooltip } from "./Tooltip";
const variations: Record<
string,
{ label: ParagraphVariant; value: ParagraphVariant }
> = {
const variations = {
primary: {
label: "extra-small/bright",
value: "extra-small",
@@ -15,7 +12,7 @@ const variations: Record<
label: "extra-extra-small/caps",
value: "extra-small/bright",
},
};
} as const;
type LabelValueStackProps = {
label: React.ReactNode;
@@ -52,11 +49,7 @@ export function LabelValueStack({
side="bottom"
button={
<Paragraph variant={variation.value}>
<a
href={href}
className="underline underline-offset-2"
target="_blank"
>
<a href={href} className="underline underline-offset-2" target="_blank">
{value}
<ArrowTopRightOnSquareIcon className="ml-1 inline-block h-4 w-4 text-dimmed" />
</a>
@@ -6,11 +6,7 @@ type Props = Parameters<typeof Link>[0] & {
disabledClassName?: string;
};
export function LinkDisabled({
disabled = false,
disabledClassName,
...props
}: Props) {
export function LinkDisabled({ disabled = false, disabledClassName, ...props }: Props) {
if (disabled) {
return (
<span {...props} className={cn(props.className, disabledClassName)}>
@@ -13,10 +13,7 @@ type LocaleContextProviderProps = {
const Context = createContext<LocaleContext | null>(null);
export const LocaleContextProvider = ({
locales,
children,
}: LocaleContextProviderProps) => {
export const LocaleContextProvider = ({ locales, children }: LocaleContextProviderProps) => {
const value = { locales };
return <Context.Provider value={value}>{children}</Context.Provider>;
@@ -1,15 +1,13 @@
import {
ExclamationCircleIcon,
ArrowTopRightOnSquareIcon,
ExclamationTriangleIcon,
InformationCircleIcon,
StopIcon,
} from "@heroicons/react/20/solid";
import { CompanyIcon, hasIcon } from "@trigger.dev/companyicons";
import {
ArrowLeftIcon,
ArrowPathIcon,
ArrowRightIcon,
ArrowTopRightOnSquareIcon,
BeakerIcon,
BellAlertIcon,
BoltIcon,
@@ -17,6 +15,7 @@ import {
BuildingOffice2Icon,
CalendarDaysIcon,
ChatBubbleLeftEllipsisIcon,
CheckCircleIcon,
CheckIcon,
ChevronDownIcon,
ChevronLeftIcon,
@@ -39,6 +38,7 @@ import {
MagnifyingGlassIcon,
PlusIcon,
PlusSmallIcon,
QrCodeIcon,
SquaresPlusIcon,
StarIcon,
UserCircleIcon,
@@ -46,119 +46,74 @@ import {
UserIcon,
UserPlusIcon,
WrenchScrewdriverIcon,
XCircleIcon,
XMarkIcon,
} from "@heroicons/react/24/solid";
import { CompanyIcon, hasIcon } from "@trigger.dev/companyicons";
import { HourglassIcon } from "lucide-react";
import { DynamicTriggerIcon } from "~/assets/icons/DynamicTriggerIcon";
import { ErrorIcon } from "~/assets/icons/ErrorIcon";
import { ScheduleIcon } from "~/assets/icons/ScheduleIcon";
import { WebhookIcon } from "~/assets/icons/WebhookIcon";
import { cn } from "~/utils/cn";
import { LogoIcon } from "../LogoIcon";
import { Spinner } from "./Spinner";
import { HourglassIcon } from "lucide-react";
import { DynamicTriggerIcon } from "~/assets/icons/DynamicTriggerIcon";
const icons = {
account: (className: string) => (
<UserCircleIcon className={cn("text-slate-400", className)} />
),
"arrow-right": (className: string) => (
<ArrowRightIcon className={cn("text-white", className)} />
),
"arrow-left": (className: string) => (
<ArrowLeftIcon className={cn("text-white", className)} />
),
background: (className: string) => (
<CloudIcon className={cn("text-sky-400", className)} />
),
beaker: (className: string) => (
<BeakerIcon className={cn("text-purple-500", className)} />
),
billing: (className: string) => (
<CreditCardIcon className={cn("text-teal-500", className)} />
),
account: (className: string) => <UserCircleIcon className={cn("text-slate-400", className)} />,
active: (className: string) => <CheckCircleIcon className={cn("text-green-500", className)} />,
"arrow-right": (className: string) => <ArrowRightIcon className={cn("text-white", className)} />,
"arrow-left": (className: string) => <ArrowLeftIcon className={cn("text-white", className)} />,
background: (className: string) => <CloudIcon className={cn("text-sky-400", className)} />,
beaker: (className: string) => <BeakerIcon className={cn("text-purple-500", className)} />,
billing: (className: string) => <CreditCardIcon className={cn("text-teal-500", className)} />,
calendar: (className: string) => (
<CalendarDaysIcon className={cn("text-purple-500", className)} />
),
check: (className: string) => (
<CheckIcon className={cn("text-dimmed", className)} />
),
check: (className: string) => <CheckIcon className={cn("text-dimmed", className)} />,
"chevron-down": (className: string) => (
<ChevronDownIcon className={cn("text-dimmed", className)} />
),
"chevron-up": (className: string) => (
<ChevronUpIcon className={cn("text-dimmed", className)} />
),
"chevron-up": (className: string) => <ChevronUpIcon className={cn("text-dimmed", className)} />,
"chevron-left": (className: string) => (
<ChevronLeftIcon className={cn("text-dimmed", className)} />
),
"chevron-right": (className: string) => (
<ChevronRightIcon className={cn("text-dimmed", className)} />
),
countdown: (className: string) => (
<HourglassIcon className={cn("text-amber-400", className)} />
),
clock: (className: string) => (
<ClockIcon className={cn("text-cyan-500", className)} />
),
close: (className: string) => (
<XMarkIcon className={cn("text-dimmed", className)} />
),
countdown: (className: string) => <HourglassIcon className={cn("text-amber-400", className)} />,
clock: (className: string) => <ClockIcon className={cn("text-cyan-500", className)} />,
close: (className: string) => <XMarkIcon className={cn("text-dimmed", className)} />,
"connection-alert": (className: string) => (
<BellAlertIcon className={cn("text-rose-500", className)} />
),
docs: (className: string) => (
<BookOpenIcon className={cn("text-slate-400", className)} />
),
dynamic: (className: string) => (
<DynamicTriggerIcon className={cn("text-cyan-500", className)} />
),
error: (className: string) => (
<ExclamationCircleIcon className={cn("text-rose-500", className)} />
<BellAlertIcon className={cn("text-amber-500", className)} />
),
docs: (className: string) => <BookOpenIcon className={cn("text-slate-400", className)} />,
dynamic: (className: string) => <DynamicTriggerIcon className={cn("text-cyan-500", className)} />,
error: (className: string) => <ErrorIcon className={cn("text-rose-500", className)} />,
"external-link": (className: string) => (
<ArrowTopRightOnSquareIcon className={cn("text-dimmed", className)} />
),
flag: (className: string) => (
<FlagIcon className={cn("text-sky-500", className)} />
),
folder: (className: string) => (
<FolderIcon className={cn("text-indigo-600", className)} />
),
envelope: (className: string) => (
<EnvelopeIcon className={cn("text-cyan-500", className)} />
),
environment: (className: string) => (
<KeyIcon className={cn("text-yellow-500", className)} />
),
globe: (className: string) => (
<GlobeAltIcon className={cn("text-fuchsia-600", className)} />
),
flag: (className: string) => <FlagIcon className={cn("text-sky-500", className)} />,
folder: (className: string) => <FolderIcon className={cn("text-indigo-600", className)} />,
envelope: (className: string) => <EnvelopeIcon className={cn("text-cyan-500", className)} />,
environment: (className: string) => <KeyIcon className={cn("text-yellow-500", className)} />,
globe: (className: string) => <GlobeAltIcon className={cn("text-fuchsia-600", className)} />,
"hand-raised": (className: string) => (
<HandRaisedIcon className={cn("text-amber-400", className)} />
),
heart: (className: string) => (
<HeartIcon className={cn("text-rose-500", className)} />
),
id: (className: string) => (
<FingerPrintIcon className={cn("text-rose-200", className)} />
),
info: (className: string) => (
<InformationCircleIcon className={cn("text-blue-500", className)} />
),
heart: (className: string) => <HeartIcon className={cn("text-rose-500", className)} />,
id: (className: string) => <FingerPrintIcon className={cn("text-rose-200", className)} />,
inactive: (className: string) => <XCircleIcon className={cn("text-rose-500", className)} />,
info: (className: string) => <InformationCircleIcon className={cn("text-blue-500", className)} />,
integration: (className: string) => (
<SquaresPlusIcon className={cn("text-teal-500", className)} />
),
"invite-member": (className: string) => (
<UserPlusIcon className={cn("text-indigo-500", className)} />
),
job: (className: string) => (
<WrenchScrewdriverIcon className={cn("text-teal-500", className)} />
),
key: (className: string) => (
<KeyIcon className={cn("text-amber-400", className)} />
),
lightbulb: (className: string) => (
<LightBulbIcon className={cn("text-amber-400", className)} />
),
job: (className: string) => <WrenchScrewdriverIcon className={cn("text-teal-500", className)} />,
key: (className: string) => <KeyIcon className={cn("text-amber-400", className)} />,
lightbulb: (className: string) => <LightBulbIcon className={cn("text-amber-400", className)} />,
log: (className: string) => (
<ChatBubbleLeftEllipsisIcon className={cn("text-slate-400", className)} />
),
@@ -166,42 +121,22 @@ const icons = {
organization: (className: string) => (
<BuildingOffice2Icon className={cn("text-fuchsia-600", className)} />
),
plus: (className: string) => (
<PlusIcon className={cn("text-green-600", className)} />
),
plus: (className: string) => <PlusIcon className={cn("text-green-600", className)} />,
"plus-small": (className: string) => (
<PlusSmallIcon className={cn("text-green-600", className)} />
),
property: (className: string) => (
<Cog8ToothIcon className={cn("text-slate-600", className)} />
),
refresh: (className: string) => (
<ArrowPathIcon className={cn("text-bright", className)} />
),
search: (className: string) => (
<MagnifyingGlassIcon className={cn("text-dimmed", className)} />
),
settings: (className: string) => (
<Cog8ToothIcon className={cn("text-slate-600", className)} />
),
spinner: (className: string) => (
<Spinner className={className} color="blue" />
),
"spinner-white": (className: string) => (
<Spinner className={className} color="white" />
),
star: (className: string) => (
<StarIcon className={cn("text-yellow-500", className)} />
),
team: (className: string) => (
<UserGroupIcon className={cn("text-blue-500", className)} />
),
trigger: (className: string) => (
<BoltIcon className={cn("text-orange-500", className)} />
),
user: (className: string) => (
<UserIcon className={cn("text-blue-600", className)} />
),
property: (className: string) => <Cog8ToothIcon className={cn("text-slate-600", className)} />,
"qr-code": (className: string) => <QrCodeIcon className={cn("text-amber-400", className)} />,
refresh: (className: string) => <ArrowPathIcon className={cn("text-bright", className)} />,
search: (className: string) => <MagnifyingGlassIcon className={cn("text-dimmed", className)} />,
settings: (className: string) => <Cog8ToothIcon className={cn("text-slate-600", className)} />,
spinner: (className: string) => <Spinner className={className} color="blue" />,
"spinner-white": (className: string) => <Spinner className={className} color="white" />,
star: (className: string) => <StarIcon className={cn("text-yellow-500", className)} />,
stop: (className: string) => <StopIcon className={cn("text-rose-500", className)} />,
team: (className: string) => <UserGroupIcon className={cn("text-blue-500", className)} />,
trigger: (className: string) => <BoltIcon className={cn("text-orange-500", className)} />,
user: (className: string) => <UserIcon className={cn("text-blue-600", className)} />,
warning: (className: string) => (
<ExclamationTriangleIcon className={cn("text-amber-400", className)} />
),
@@ -209,8 +144,11 @@ const icons = {
"custom-event": (className: string) => (
<CodeBracketSquareIcon className={cn("text-toxic-600", className)} />
),
"register-source": (className: string) => (
<GlobeAltIcon className={cn("text-sky-500", className)} />
),
"schedule-interval": (className: string) => (
<ScheduleIcon className={cn("text-sky-500", className)} />
<ClockIcon className={cn("text-sky-500", className)} />
),
"schedule-cron": (className: string) => (
<ScheduleIcon className={cn("text-sky-500", className)} />
@@ -218,12 +156,11 @@ const icons = {
"schedule-dynamic": (className: string) => (
<ScheduleIcon className={cn("text-sky-500", className)} />
),
webhook: (className: string) => (
<WebhookIcon className={cn("text-pink-500", className)} />
),
webhook: (className: string) => <WebhookIcon className={cn("text-pink-500", className)} />,
};
export type IconNames = keyof typeof icons;
export type IconNamesOrString = IconNames | (string & {});
export const iconNames = Object.keys(icons) as IconNames[];
export function NamedIcon({
@@ -231,7 +168,7 @@ export function NamedIcon({
className,
fallback,
}: {
name: string;
name: IconNamesOrString;
className: string;
fallback?: JSX.Element;
}) {
@@ -241,12 +178,7 @@ export function NamedIcon({
if (hasIcon(name)) {
return (
<div
className={cn(
"grid aspect-square min-h-fit place-items-center",
className
)}
>
<span className={cn("grid aspect-square min-h-fit place-items-center", className)}>
<CompanyIcon
name={name}
className={"h-full w-full p-[7%]"}
@@ -255,7 +187,7 @@ export function NamedIcon({
shapeRendering: "geometricPrecision",
}}
/>
</div>
</span>
);
}
@@ -273,10 +205,12 @@ export function NamedIconInBox({
name,
className,
fallback,
iconClassName,
}: {
name: string;
className?: string;
fallback?: JSX.Element;
iconClassName?: string;
}) {
return (
<div
@@ -285,7 +219,7 @@ export function NamedIconInBox({
className
)}
>
<NamedIcon name={name} fallback={fallback} className="h-6 w-6" />
<NamedIcon name={name} fallback={fallback} className={cn("h-6 w-6", iconClassName)} />
</div>
);
}
@@ -23,9 +23,7 @@ export const OperatingSystemContextProvider = ({
};
const throwIfNoProvider = () => {
throw new Error(
"Please wrap your application in an OperatingSystemContextProvider."
);
throw new Error("Please wrap your application in an OperatingSystemContextProvider.");
};
export const useOperatingSystem = () => {
@@ -11,17 +11,9 @@ type WithChildren = {
children: React.ReactNode;
};
export function PageHeader({
children,
hideBorder,
}: WithChildren & { hideBorder?: boolean }) {
export function PageHeader({ children, hideBorder }: WithChildren & { hideBorder?: boolean }) {
return (
<div
className={cn(
"mx-4 pt-4",
hideBorder ? "" : "border-b border-slate-800 pb-4"
)}
>
<div className={cn("mx-4 pt-4", hideBorder ? "" : "border-b border-slate-800 pb-4")}>
{children}
</div>
);
@@ -49,10 +41,7 @@ export function PageTitle({ title, backButton }: PageTitleProps) {
className="flex items-center gap-1 text-slate-400 transition group-hover:text-white"
>
<ChevronLeftIcon className="h-6" />
<Header1
variant="dimmed"
className="transition group-hover:text-white"
>
<Header1 textColor="dimmed" className="transition group-hover:text-white">
{backButton.text}
</Header1>
</Link>
@@ -107,24 +96,13 @@ export function PageInfoProperty({
}) {
return (
<div className="flex items-center gap-1">
{icon && typeof icon === "string" ? (
<NamedIcon name={icon} className="h-4 w-4" />
) : (
icon
)}
{icon && typeof icon === "string" ? <NamedIcon name={icon} className="h-4 w-4" /> : icon}
{label && (
<Paragraph
variant="extra-small/caps"
className="mt-0.5 whitespace-nowrap"
>
<Paragraph variant="extra-small/caps" className="mt-0.5 whitespace-nowrap">
{label}:
</Paragraph>
)}
{typeof value === "string" || typeof value === "number" ? (
<Paragraph variant="small">{value}</Paragraph>
) : (
value
)}
<Paragraph variant="small">{value}</Paragraph>
</div>
);
}
@@ -43,11 +43,8 @@ export function PaginationControls({
<div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
<div>
<p className="text-sm text-slate-400">
Showing{" "}
<span className="font-medium">
{(currentPage - 1) * pageSize + 1}
</span>{" "}
to <span className="font-medium">{currentPage * pageSize}</span> of{" "}
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{" "}
<span className="font-medium">{currentPage * pageSize}</span> of{" "}
<span className="font-medium">{totalResults}</span> results
</p>
</div>
@@ -86,10 +83,7 @@ export function PaginationControls({
);
}
function pageUrl(
location: ReturnType<typeof useLocation>,
page: number
): string {
function pageUrl(location: ReturnType<typeof useLocation>, page: number): string {
const search = new URLSearchParams(location.search);
search.set("page", String(page));
@@ -101,8 +95,7 @@ const baseClass =
"relative inline-flex items-center border px-3.5 py-2 text-xs font-medium focus:z-20 transition";
const unselectedClass =
"bg-slate-700/20 border-slate-500 text-slate-400 hover:bg-slate-400 hover:text-slate-900";
const selectedClass =
"z-10 bg-slate-500 border-slate-500 hover:bg-slate-400 text-slate-900";
const selectedClass = "z-10 bg-slate-500 border-slate-500 hover:bg-slate-400 text-slate-900";
function PageLinkComponent({
page,
@@ -114,19 +107,13 @@ function PageLinkComponent({
if (page.type === "specific") {
if (page.isCurrent) {
return (
<Link
to={pageUrl(location, page.page)}
className={cn(baseClass, selectedClass)}
>
<Link to={pageUrl(location, page.page)} className={cn(baseClass, selectedClass)}>
{page.page}
</Link>
);
} else {
return (
<Link
to={pageUrl(location, page.page)}
className={cn(baseClass, unselectedClass)}
>
<Link to={pageUrl(location, page.page)} className={cn(baseClass, unselectedClass)}>
{page.page}
</Link>
);
@@ -154,10 +141,7 @@ type SpecificPageLink = {
// If there are less than or equal to 6 pages, just show all the pages.
// If there are more than 5 pages, show the first 3, the current page, and the last 3.
function calculatePageLinks(
currentPage: number,
totalPages: number
): Array<PageLink> {
function calculatePageLinks(currentPage: number, totalPages: number): Array<PageLink> {
const pageLinks: Array<PageLink> = [];
if (totalPages <= 10) {

Some files were not shown because too many files have changed in this diff Show More