1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-27 01:19:00 +02:00
Commit Graph

6041 Commits

Author SHA1 Message Date
Tymoteusz Czech
b111abc96f
feat: update sidebar navigation - refactors (#10037)
- added `sideMenuCleanup` flag
- extracted `SecondaryNavigation`, `SecondaryNavigationList` and
`MobileNavigationSidebar` into separate files
- hidden recent projects and flags
- renamed 'Insights' to 'Analytics'
2025-05-28 12:00:28 +02:00
dependabot[bot]
e65865d410
chore(deps-dev): bump http-proxy-middleware from 2.0.7 to 2.0.9 in /frontend (#9786)
Bumps
[http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware)
from 2.0.7 to 2.0.9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/chimurai/http-proxy-middleware/releases">http-proxy-middleware's
releases</a>.</em></p>
<blockquote>
<h2>v2.0.9</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(fixRequestBody): check readableLength by <a
href="https://github.com/chimurai"><code>@​chimurai</code></a> in <a
href="https://redirect.github.com/chimurai/http-proxy-middleware/pull/1097">chimurai/http-proxy-middleware#1097</a></li>
<li>chore(package): v2.0.9 by <a
href="https://github.com/chimurai"><code>@​chimurai</code></a> in <a
href="https://redirect.github.com/chimurai/http-proxy-middleware/pull/1099">chimurai/http-proxy-middleware#1099</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/chimurai/http-proxy-middleware/compare/v2.0.8...v2.0.9">https://github.com/chimurai/http-proxy-middleware/compare/v2.0.8...v2.0.9</a></p>
<h2>v2.0.8</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(fixRequestBody): prevent multiple .write() calls by <a
href="https://github.com/chimurai"><code>@​chimurai</code></a> in <a
href="https://redirect.github.com/chimurai/http-proxy-middleware/pull/1090">chimurai/http-proxy-middleware#1090</a></li>
<li>fix(fixRequestBody): handle invalid request by <a
href="https://github.com/chimurai"><code>@​chimurai</code></a> in <a
href="https://redirect.github.com/chimurai/http-proxy-middleware/pull/1091">chimurai/http-proxy-middleware#1091</a></li>
<li>chore(package): v2.0.8 by <a
href="https://github.com/chimurai"><code>@​chimurai</code></a> in <a
href="https://redirect.github.com/chimurai/http-proxy-middleware/pull/1094">chimurai/http-proxy-middleware#1094</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.8">https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.8</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md">http-proxy-middleware's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/chimurai/http-proxy-middleware/releases/tag/v2.0.9">v2.0.9</a></h2>
<ul>
<li>fix(fixRequestBody): check readableLength</li>
</ul>
<h2><a
href="https://github.com/chimurai/http-proxy-middleware/releases/tag/v2.0.8">v2.0.8</a></h2>
<ul>
<li>fix(fixRequestBody): prevent multiple .write() calls</li>
<li>fix(fixRequestBody): handle invalid request</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="617a7c9da9"><code>617a7c9</code></a>
chore(package): v2.0.9 (<a
href="https://redirect.github.com/chimurai/http-proxy-middleware/issues/1099">#1099</a>)</li>
<li><a
href="d22d587648"><code>d22d587</code></a>
fix(fixRequestBody): check readableLength (<a
href="https://redirect.github.com/chimurai/http-proxy-middleware/issues/1097">#1097</a>)</li>
<li><a
href="d03d51b54a"><code>d03d51b</code></a>
chore(package): v2.0.8 (<a
href="https://redirect.github.com/chimurai/http-proxy-middleware/issues/1094">#1094</a>)</li>
<li><a
href="c50dd06d91"><code>c50dd06</code></a>
fix(fixRequestBody): handle invalid request (<a
href="https://redirect.github.com/chimurai/http-proxy-middleware/issues/1091">#1091</a>)</li>
<li><a
href="76a9d8d6dc"><code>76a9d8d</code></a>
fix(fixRequestBody): prevent multiple .write() calls (<a
href="https://redirect.github.com/chimurai/http-proxy-middleware/issues/1090">#1090</a>)</li>
<li>See full diff in <a
href="https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=http-proxy-middleware&package-manager=npm_and_yarn&previous-version=2.0.7&new-version=2.0.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/Unleash/unleash/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 17:58:42 +02:00
Nuno Góis
a419b8e098
chore: prefer searchEvents over deprecated methods (#10031)
https://linear.app/unleash/issue/2-3577/prefer-the-new-searchevents-method-over-deprecated-methods

We should favor our new `searchEvents` method over the other deprecated
methods. This PR removes these deprecated methods, replaces them with
the new `searchEvents` and `searchEventsCount` methods, and marks the
old endpoints as deprecated.

Follow-up to: https://github.com/Unleash/unleash/pull/10030
2025-05-27 16:19:10 +01:00
Thomas Heartman
3e57c4803c
Chore(1-3755)/split insights in three (#10035)
Creates sections for the insights dashboard and moves charts around into
the same order as the sketches and into the right sections. There's no
charts for the top section (lifecycle currently) yet, and the sections
also don't have their own filters.

To make this re-ordering easier, I've also moved the previous insights
chart into a legacy file and set up a proxy component that handles
switching based on the flag.


![image](https://github.com/user-attachments/assets/f0929998-def3-4643-babd-ab53f4ea8e98)


Next step is separating the filters.
2025-05-27 15:06:48 +02:00
Thomas Heartman
5df074bd14
Remove entire row for median time to production (#10034)
Removes the "median time to production" snapshot + graph when the
lifecycleMetrics flag is active.

In other words: this entire box is gone 💨 
<img width="1326" alt="image"
src="https://github.com/user-attachments/assets/929a9097-82a6-493d-b0dd-614000ffcfe7"
/>
2025-05-27 14:00:08 +02:00
Thomas Heartman
092c525531
chore: add lifecycleMetrics flag definition (#10033)
Adds the new lifecycleMetrics flag.
2025-05-27 11:53:22 +00:00
David Leek
9fca29f254
feat: custom metrics (#10022) 2025-05-22 09:58:54 +02:00
Tymoteusz Czech
b0954f213c
chore: remove flagsReleaseManagementUI and flagsOverviewSearch flags (#10011)
Removing the `flagsReleaseManagementUI` and `flagsOverviewSearch`
feature flags - we're keeping these enabled.
2025-05-16 15:13:32 +02:00
Jaanus Sellin
a08db953b8
fix: now feature component is not loaded before we have feature infor… (#10012)
We were seeing strange errors when the feature component was rendered
before the feature data was returned from the backend. Now, we ensure
the component is not rendered until the feature is available.
2025-05-16 16:03:26 +03:00
Gastón Fournier
beb29f5b5b
chore: stop using deprecated properties and lean on resourceLimits cfg (#9994)
This removes segmentValuesLimit and strategySegmentsLimit from
ui-config-schema, deprecated in 5.11.0
2025-05-16 09:41:04 +00:00
Nuno Góis
995d69a352
fix: hide project archive in OSS (#10004)
https://linear.app/unleash/issue/2-3569/fix-hide-project-archive-in-oss

Hides "project archive" in OSS.

I believe this is a bug. OSS only has one project and the project
archive was acting unexpectedly anyways since it was showing the same
default project as being archived. This is because in OSS we use the OSS
project-controller, not the Enterprise version (override) of it.
2025-05-15 14:45:29 +01:00
Thomas Heartman
ffdf85c8b8
Fix: deleted legal values not being cleared when you select new ones (#9986)
Updates how we handle deleted legal values for the constraint reducer.
The previous iteration used useState and took the deleted legal values
as a third argument. This isn't possible anymore because a reducer can
take only two args. The simplest way forward for this was to move the
deleted legal values into the state itself, so that it's available in
the reducer. Because deleted legal values can be updated whenever (when
we get a response for that specific context field), we'll update it via
`useEffect`.

I'm not crazy about this approach, so if you have better suggestions,
I'm listening.

I've changed the signature of the reducer, so I've also updated the
tests. In doing so, I thought it now makes more sense to have the base
objects be objects instead of functions, so the changes there are
primarily updating that.
2025-05-15 13:06:08 +00:00
Thomas Heartman
e09b839ac0
chore: disable delete button if a context field has usage (#10002)
Blocks deletion of context fields from the UI if the context field has
any usage.

<img width="1399" alt="image"
src="https://github.com/user-attachments/assets/e6c26671-761b-4e54-9850-c505ba7b42f3"
/>
2025-05-15 13:55:56 +02:00
Thomas Heartman
082a03afd7
Fix(1-3485)/handle deleted constraints (#9999)
Improves handling of constraints in use that have been deleted.

This change implments a few small changes on both the front and the back
end on how we deal with constraints that have been deleted.

The most important change is on the back end, in the
`/constraints/validate` endpoint. We used to throw here if the
constraint couldn't be found, but the only reason we wanted to look for
the constraint in the db was to check for legal values. Now, instead,
we'll allow you to pass a constraint field that doesn't exist in the
database. We'll still check the values against the operator for
validity, we just don't control legal values anymore (because there
aren't any).

On the front end, we improve the handling by showing the deleted context
filed in the dropdown, both when the selector dropdown is closed and
when it is open. However, if you change the context field, we remove the
deleted field from the list. This seems like a sensible tradeoff. Means
you can't select it if you've deselected it.
2025-05-15 13:08:54 +02:00
Nuno Góis
480689e828
chore: revive archive page (#10001)
Related to:

-
https://linear.app/unleash/issue/2-3366/remove-get-apiadminarchivefeatures-deprecated-in-4100
-
https://linear.app/unleash/issue/2-3367/remove-get-apiadminarchivefeaturesprojectid-deprecated-in-4110

Brings back the overall flag archive page and table using the feature
flag search endpoint.
2025-05-15 11:54:52 +01:00
Jaanus Sellin
0255cf137a
fix: now feature is not pulled from server if not defined (#10000) 2025-05-15 11:54:40 +03:00
Jaanus Sellin
9aa0c4c738
feat: constraints that are in recents will have generated key (#9996)
Previously it was trying to get constraint id for key, but id did not
exist. Now for unique keys, I am using the hashed constraint payload.
2025-05-15 10:50:59 +03:00
Thomas Heartman
a2723ec0c0
chore remove and clean some stuff (#9993)
- Deletes an unused file
- Fixes a console.error due to a prop being passed on to the HTML
component
- Puts all editable constraint files within a separate directory.
2025-05-15 07:23:33 +00:00
Thomas Heartman
a80b667cf5
chore(1-3747): minor strategy edit header updates (#9992)
Makes a few small changes to the strategy header:
- Removes the rollout percentage and environment indicator
- Changes the env disabled alert from warning to info
- Maybe moves the alert to the end of the form, only on the general tab
panel

I've placed the removal of the rollout percentage and env header behind
a flag, but not the alert changes.

Before:

![image](https://github.com/user-attachments/assets/29382afe-9a75-4138-be1c-2bab45a75787)


After:
<img width="1239" alt="image"
src="https://github.com/user-attachments/assets/db73c9c8-3244-40db-9991-c412c9aadc18"
/>
2025-05-14 12:48:46 +00:00
Jaanus Sellin
ba80c925e7
feat: now pressing enter will submit the new rollout percentage (#9995) 2025-05-14 15:01:55 +03:00
Jaanus Sellin
a0e4f08507
feat: now updating strategy also saves recents (#9990)
The issue was that processing constraints after the API call in
updateStrategyOnFeature caused React's state updates to be interrupted
before they could be persisted to localStorage, but by moving that
processing before the API call, we ensure constraints are saved
immediately, regardless of API timing.
2025-05-14 15:01:11 +03:00
Thomas Heartman
26a1156959
1-3744: Set a min-width for context field selector + underline when not focused (#9991)
When you have very short names (1--2 characters) the field itself can be
hard to see, so we'll set a min width.

Before (with hover): 
<img width="455" alt="image"
src="https://github.com/user-attachments/assets/a9590d3d-88a2-4814-9581-33b8378f4524"
/>

After (without hover):
<img width="523" alt="image"
src="https://github.com/user-attachments/assets/1e7b6028-e142-442a-9b53-55425d2d6c59"
/>
2025-05-14 11:56:05 +00:00
renovate[bot]
d17d68d6aa
chore(deps): update node.js to v22 (#9487) 2025-05-14 10:31:18 +00:00
Mateusz Kwasniewski
1523e2d056
feat: plausible tracking for links (#9988) 2025-05-14 11:19:37 +02:00
Gastón Fournier
abe160eb7d
feat: Unleash v7 ESM migration (#9877)
We're migrating to ESM, which will allow us to import the latest
versions of our dependencies.

Co-Authored-By: Christopher Kolstad <chriswk@getunleash.io>
2025-05-14 09:47:12 +02:00
Thomas Heartman
c358a8ffd3
Make hover underline purple (#9985)
Uses a purple color for the hover underline. Also, sets it to be
transparent when not-hovered, so that you get a nice fade in effect.

Focus (top) and hover (bottom) now have the same visual style, but
different ways to get to that state (expansion vs fade-in):
<img width="979" alt="image"
src="https://github.com/user-attachments/assets/e342ea4e-4821-4e4c-bb5d-6b9d3a672e26"
/>
2025-05-14 06:57:20 +00:00
Jaanus Sellin
aa885b9afd
feat: now recently used constraints are not shown if already in use (#9984) 2025-05-14 09:53:50 +03:00
Jaanus Sellin
9bd69a852e
feat: now only recents show segments that are not being used currently (#9983)
Now only recents show segments that are not being used currently
2025-05-14 09:40:33 +03:00
Thomas Heartman
96a388298f
fix(1-3740): Don't autofocus the editable constraint field. (#9982)
There can be any number of these on a page, so setting autofocus here is
a Bad Idea (TM). It's probably a holdover from when the input was an
accordion and we wanted to give you focus when you opened it (we do a
similar thing for the popover, for instance).

This property will cause you to focus (and potentially scroll) to the
last constraint on the page.
2025-05-14 08:04:47 +02:00
Thomas Heartman
4ea2499ce7
Chore(1-3688): improve performance for large lists of legal values (#9978)
This PR implements a number of strategies to make the app perform better
when you have large lists. For instance, we have a constraint field that
has ~600 legal values. Currently in main, it is pretty slow and sloggy
to use (about on par with what we see in hosted).

With this PR, it becomes pretty snappy, as shown in this video (should
hopefully be even better in production mode?):


https://www.loom.com/share/2e882bee25a3454a85bec7752e8252dc?sid=7786b22d-6c60-47e8-bd71-cc5f347c4e0f

The steps taken are:
1. Change the `useState` hook to instead use `useReducer`. The reason is
that its dispatch function is guaranteed to have a stable identity. This
lets us use it in memoized functions and components.

2. Because useReducer doesn't update the state variable until the next
render, we need to use `useEffect` to update the constraint when it has
actually updated instead of just calling it after the reducer.

3. Add a `toggle value` action and use that instead of checking whether
the value is equal or not inside an onChange function. If we were to
check the state of the value outside the reducer, the memoized function
would be re-evaluated every time value or values change, which would
result in more renders than necessary. By instead doing this kind of
checking inside the reducer, we can cache more aggressively.

4. Because the onChange function can now be memoized, we can memoize all
the legal value selector labels too. This is the real goal here, because
we don't need to re-render 600 components, because one of them was
checked.

One side effect of using useEffect to call `onUpdate` is that it will
also be called immediately when the hook is invoked the first time, but
it will be called with the same value as the constraint that was passed
in, so I don't think that's an issue.

Second: the `useEffect` call uses `localConstraint` directly as a dep
instead of stringifying it. I'm not sure why, but stringifying it makes
it not update correctly for legal values.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-14 08:04:39 +02:00
Nuno Góis
e7da79d974
refactor: clean up dead code after removing deprecated project role access endpoint (#9949)
https://linear.app/unleash/issue/2-3363/remove-post-apiadminprojectsprojectidroleroleidaccess-deprecated-in

Cleans up dead code after removing POST
`/api/admin/projects/{projectId}/role/{roleId}/access` which was
deprecated in v5.5.

Should only be merged after the Enterprise PR.
2025-05-13 13:52:13 +01:00
Fredrik Strand Oseberg
94c73bbc5d
feat: event log environment filter (#9979)
Add Environment Filter to Event Log
2025-05-13 13:45:22 +02:00
Nuno Góis
c0c7005859
refactor: remove deprecated post project role access endpoint (#9948)
https://linear.app/unleash/issue/2-3363/remove-post-apiadminprojectsprojectidroleroleidaccess-deprecated-in

Removes POST `/api/admin/projects/{projectId}/role/{roleId}/access`
which was deprecated in v5.5.
Also cleans up related code.

Needs follow up PRs.
2025-05-13 12:28:44 +01:00
Nuno Góis
0429c1985a
refactor: remove deprecated get archive featured by project endpoint (#9938)
https://linear.app/unleash/issue/2-3367/remove-get-apiadminarchivefeaturesprojectid-deprecated-in-4110

Removes GET `/api/admin/archive/features/{projectId}` which was
deprecated in v4.11.
Also cleans up related code.

I leveraged our search features endpoint where needed, since that's what
we're using now in our UI (to see archived features you filter by
archived=true).

Builds on top of https://github.com/Unleash/unleash/pull/9924 — Since
they're both related.
2025-05-13 12:09:52 +01:00
Nuno Góis
42b6fc810e
refactor: remove deprecated GET archive features endpoint (#9924)
https://linear.app/unleash/issue/2-3366/remove-get-apiadminarchivefeatures-deprecated-in-4100

Removes GET `/api/admin/archive/features` which was deprecated in v4.10.
Also cleans up related code.

May include some slight scouting.

**P.S.** Should we merge this into main, or is there a `v7` branch we
should be targeting instead?
2025-05-13 11:45:03 +01:00
Jaanus Sellin
5f4d7de62e
chore: fix rendering issue without setConstraints (#9975)
The current implementation states that if you do not have
access—typically as a viewer user—it renders the old component.
Rendering an empty view is acceptable, as this is part of the segment
creation flow, and if you do not have permissions, you do not need to
see it.
2025-05-13 12:41:19 +03:00
Thomas Heartman
257b8d1f40
test/refactor: useEditableConstraint hook (#9970)
Adds a test suite for the useEditableConstraint hook, attempting to test
all the parts of it that we can't test in isolation.

Plus: a few, small refactorings:
- Renames `onAutoSave` on `onUpdate` to better match `onDelete` (and
because autosave doesn't really mean anything anymore).
- Simplifies and collapses some types
2025-05-13 11:30:07 +02:00
Jaanus Sellin
3c42edfbe8
chore: fix setConstraints being undefined/null (#9972) 2025-05-13 12:20:15 +03:00
Tymoteusz Czech
82dddb2eef
feat: update flags overview status column (#9961)
Improved spacing in the flag overview columns.
2025-05-13 09:37:22 +02:00
Jaanus Sellin
31a23db05e
chore: clarify deleted legal values warning (#9969) 2025-05-13 10:19:11 +03:00
Thomas Heartman
8bf3b1f135
test(1-3734): test constraint reducer (#9966)
Adds a fairly comprehensive test suite for the constraint reducer. I put
in all cases I thought were relevant.

As part of this, I discovered one bug, and changed two actions.

The bug was toggling on the wrong property when you tried to invert case
sensitivity.

The action changes are:
- rename "remove value from list" to "remove value"
- remove "set value" in favor of instead letting "add value(s)" work in
single-value constraints too.
2025-05-13 09:02:38 +02:00
Gastón Fournier
3e0b127113
chore: remove proxy from integrations (#9962)
Removing proxy from integrations as it's been deprecated while ago and
currently unmaintained
2025-05-12 16:37:18 +02:00
Thomas Heartman
d0975c52a9
Test(1-3733): get invalid and deleted legal values (#9963)
Extracts and tests the implementation of the functions to get deleted
and invalid legal values.

This is pretty straightforward stuff and arguably not crucial, but it
was an easy place to start and I don't think it hurts to have these in
place anyway.
2025-05-12 12:57:55 +00:00
Gastón Fournier
8b4ad4b574
chore: remove semver version from footer and proxy from sdk list (#9956)
This will stop displaying the semver version for our hosted (i.e.
managed) customers, as it has become less relevant over time. It remains
valuable for self-hosted users.
2025-05-12 14:32:12 +02:00
Jaanus Sellin
42f3ba5fc2
feat: add input box for gradual rollout slider (#9960) 2025-05-12 15:27:31 +03:00
Mateusz Kwasniewski
d175a5705a
feat: Import feature links (#9958) 2025-05-12 13:59:18 +02:00
Fredrik Strand Oseberg
d4d6e658ff
Chore/cleanup tag color feature falg (#9959)
Removes the flag for tag type colors.
2025-05-12 13:54:38 +02:00
Thomas Heartman
6efbe2d545
Single legal values (#9935)
Adds an input form for single legal values (i.e. for number and semver
operators).

- Uses the `validator` for the constraint to check the list of legal
values and provides a list of "invalid legal values" for values that
don't pass validation.
- Values in the "invalid legal values" set are "disabled" when rendered
in the UI. Additionally, there's an extra bit of text that tells you
that values that aren't valid are disabled.
- Makes the legal values selector more generic and makes it adapt to
multi- or single-value selection based on input props. The external
interface is two separate components (to make it clearer that they are
different things and because their props don't line up perfectly).

Rendered:
<img width="957" alt="image"
src="https://github.com/user-attachments/assets/cd8d2f32-057d-4e31-8fd3-174676eeb65e"
/>


Still todo: testing deleted/invalid legal value detection. I'll do that
as part of the big testathon in a followup.
2025-05-12 13:35:45 +02:00
Tymoteusz Czech
1ccd201a25
feat: use modal for managing link templates (#9955)
Editing with modals is more focused.
2025-05-12 11:52:48 +02:00
Tymoteusz Czech
5614cb56d3
feat: ui for external link templates (#9945)
Support for project link templates to the frontend UI
2025-05-12 11:05:04 +02:00