Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion client/app/components/RpcLabel.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<BaseBadge :color="label?.color">
<Icon v-if="label?.isException" class="mr-2" name="pajamas:warning" />
<Icon v-if="label?.isException" class="mr-1" name="pajamas:warning" title="Exception" />
<Icon v-if="label?.isComplexity" class="mr-1" name="uil:layer-group" title="Complexity" />
<Icon v-if="label?.isPublic" class="mr-1" name="uil:globe" title="Public" />
{{ label?.slug }}
</BaseBadge>
</template>
Expand Down
20 changes: 19 additions & 1 deletion client/app/components/RpcLabelEditDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,23 @@
<p class="text-gray-500">This label is assignable and actively in use.</p>
</div>
</div>

<div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:py-6">
<label
for="is-public"
class="block text-sm font-medium leading-6 text-gray-900 sm:pt-1.5">
Is Public
</label>
<div class="mt-2 sm:col-span-2 sm:mt-0">
<input
id="is-public"
v-model="label.isPublic"
name="is-public"
type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" />
<p class="text-gray-500">This label is exposed on the public queue.</p>
</div>
</div>
</div>
</div>
</form>
Expand Down Expand Up @@ -177,7 +194,8 @@ const NEW_LABEL_DEFAULTS: Label = {
slug: '',
isException: false,
color: 'slate',
used: true
used: true,
isPublic: false
}

const label = reactive<Label>(props.label ? { ...props.label } : NEW_LABEL_DEFAULTS)
Expand Down
41 changes: 39 additions & 2 deletions client/app/pages/labels/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@
</button>
</div>
</div>
<div class="mt-4 flex flex-wrap items-center gap-2">
<span class="text-sm text-gray-500 dark:text-neutral-400">Filter:</span>
<button
v-for="t in filterToggles"
:key="t.key"
type="button"
:aria-pressed="filters[t.key]"
:class="[
'inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-sm transition-colors',
filters[t.key]
? 'bg-violet-100 border-violet-400 text-violet-900 dark:bg-violet-900/30 dark:border-violet-500 dark:text-violet-200'
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700'
]"
@click="filters[t.key] = !filters[t.key]">
<Icon :name="t.icon" />{{ t.label }}
</button>
</div>
<ErrorAlert v-if="labelsError" title="API Error">
API error while requesting labels: {{ labelsError }}
</ErrorAlert>
Expand All @@ -36,8 +53,28 @@ const snackbar = useSnackbar()
const sortedLabels = computed(
() => labels.value?.toSorted((a, b) => a.slug.localeCompare(b.slug, 'en')) ?? []
)
const labelsInUse = computed(() => sortedLabels.value.filter((l) => l.used))
const labelsNotInUse = computed(() => sortedLabels.value.filter((l) => !l.used))
const filterToggles = [
{ key: 'isException', label: 'Exception', icon: 'pajamas:warning' },
{ key: 'isComplexity', label: 'Complexity', icon: 'uil:layer-group' },
{ key: 'isPublic', label: 'Public', icon: 'uil:globe' }
] as const

const filters = reactive({ isException: false, isComplexity: false, isPublic: false })

// No toggle active → show all; otherwise show labels matching any active flag.
const filteredLabels = computed(() => {
if (!filters.isException && !filters.isComplexity && !filters.isPublic) {
return sortedLabels.value
}
return sortedLabels.value.filter(
(l) =>
(filters.isException && l.isException) ||
(filters.isComplexity && l.isComplexity) ||
(filters.isPublic && l.isPublic)
)
})
const labelsInUse = computed(() => filteredLabels.value.filter((l) => l.used))
const labelsNotInUse = computed(() => filteredLabels.value.filter((l) => !l.used))

const {
data: labels,
Expand Down
6 changes: 6 additions & 0 deletions purple_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4633,6 +4633,8 @@ components:
$ref: "#/components/schemas/ColorEnum"
used:
type: boolean
is_public:
type: boolean
required:
- slug
LabelRequest:
Expand All @@ -4650,6 +4652,8 @@ components:
$ref: "#/components/schemas/ColorEnum"
used:
type: boolean
is_public:
type: boolean
required:
- slug
LabelStat:
Expand Down Expand Up @@ -5134,6 +5138,8 @@ components:
$ref: "#/components/schemas/ColorEnum"
used:
type: boolean
is_public:
type: boolean
PatchedRfcAuthorRequest:
type: object
properties:
Expand Down
34 changes: 34 additions & 0 deletions rpc/migrations/0012_historicallabel_is_public_label_is_public.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright The IETF Trust 2026, All Rights Reserved

from django.db import migrations, models


def set_existing_labels_public(apps, schema_editor):
# Existing labels were all public before this flag existed; the field
# default (False) applies only to labels created afterwards.
Label = apps.get_model("rpc", "Label")
Label.objects.update(is_public=True)


def noop(apps, schema_editor):
pass


class Migration(migrations.Migration):
dependencies = [
("rpc", "0011_historicalactionholder_historicalrpcauthorcomment_and_more"),
]

operations = [
migrations.AddField(
model_name="historicallabel",
name="is_public",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="label",
name="is_public",
field=models.BooleanField(default=False),
),
migrations.RunPython(set_existing_labels_public, noop),
]
1 change: 1 addition & 0 deletions rpc/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,7 @@ class Label(models.Model):
choices=zip(TAILWIND_COLORS, TAILWIND_COLORS, strict=False),
)
used = models.BooleanField(default=True)
is_public = models.BooleanField(default=False)
history = HistoricalRecords()

def __str__(self):
Expand Down
8 changes: 8 additions & 0 deletions rpc/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ class Meta:
"is_complexity",
"color",
"used",
"is_public",
]


Expand Down Expand Up @@ -2605,6 +2606,13 @@ class PublicQueueItemSerializer(QueueItemSerializer):
disposition_name = serializers.SlugRelatedField(
source="disposition", slug_field="name", read_only=True
)
# only expose labels flagged public.
labels = serializers.SerializerMethodField()

@extend_schema_field(LabelSerializer(many=True))
def get_labels(self, obj):
public = [label for label in obj.labels.all() if label.is_public]
return LabelSerializer(public, many=True).data

@extend_schema_field(RpcRelatedDocumentSerializer(many=True))
def get_references(self, obj):
Expand Down