Link measures to third parties

Add a many-to-many relationship between measures and third parties,
surfaced as a measures tab on the third party detail page and a third
parties tab on the measure detail page. Each side gets a paginated
list with a link/unlink dialog.

Also remove the right-hand drawer on the measure detail page and
expose the state as a badge in the page header, mirroring how the
compliance page surfaces its active flag.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-05-22 16:07:48 +02:00
parent b6b1e801b1
commit 6dfdd7ca49
35 changed files with 2109 additions and 54 deletions

View File

@@ -720,3 +720,115 @@ WHERE %s
return err
}
func (m *Measures) CountByThirdPartyID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
thirdPartyID gid.GID,
filter *MeasureFilter,
) (int, error) {
q := `
WITH mtgtns AS (
SELECT
m.id,
m.tenant_id,
m.search_vector,
m.state,
m.category
FROM
measures m
INNER JOIN
measures_third_parties mtp ON m.id = mtp.measure_id
WHERE
mtp.third_party_id = @third_party_id
)
SELECT
COUNT(id)
FROM
mtgtns
WHERE %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
args := pgx.NamedArgs{"third_party_id": thirdPartyID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
row := conn.QueryRow(ctx, q, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot scan count: %w", err)
}
return count, nil
}
func (m *Measures) LoadByThirdPartyID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
thirdPartyID gid.GID,
cursor *page.Cursor[MeasureOrderField],
filter *MeasureFilter,
) error {
q := `
WITH mtgtns AS (
SELECT
m.id,
m.tenant_id,
m.organization_id,
m.category,
m.name,
m.description,
m.state,
m.reference_id,
m.search_vector,
m.created_at,
m.updated_at
FROM
measures m
INNER JOIN
measures_third_parties mtp ON m.id = mtp.measure_id
WHERE
mtp.third_party_id = @third_party_id
)
SELECT
id,
organization_id,
category,
name,
description,
state,
reference_id,
created_at,
updated_at
FROM
mtgtns
WHERE %s
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"third_party_id": thirdPartyID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query measures: %w", err)
}
measures, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Measure])
if err != nil {
return fmt.Errorf("cannot collect measures: %w", err)
}
*m = measures
return nil
}