5 Commits
8.2.0 ... 8.2.1

Author SHA1 Message Date
942403e897 version bump 8.2.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:10:19 -07:00
770f02eea5 case 4688 - user form: "Can change records" choice for a new agent token
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 14:56:22 -07:00
7bfb389a3a case 4745 - batch close extension: close as Done or Never
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:52:58 -07:00
501be1764b case 4745 - case form Status, Product and Release fields
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:52:58 -07:00
573722da39 case 4688 - user form: hide login fields for agent users; copy button for a new agent token; token box only once saved
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 19:51:56 -07:00
5 changed files with 163 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "sockeye",
"version": "8.2.0",
"version": "8.2.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View File

@@ -1,5 +1,5 @@
export default {
version: "8.2.0",
version: "8.2.1",
//case 4630: the year is set when the client is built (vue.config.js)
copyright: `© ${process.env.VUE_APP_BUILD_YEAR}, Ground Zero Tech-Works Inc.`
};

View File

@@ -7,6 +7,17 @@
>Close / reopen cases</v-expansion-panel-header
>
<v-expansion-panel-content>
<!-- case 4745: closing sets this status; reopening sets New. Duplicate is one case at a time, on the case form -->
<v-radio-group
v-model="closeAs"
row
dense
:disabled="jobActive"
data-cy="extCloseCasesAs"
>
<v-radio label="Close as Done" :value="STATUS_DONE"></v-radio>
<v-radio label="Close as Never" :value="STATUS_NEVER"></v-radio>
</v-radio-group>
<v-btn
color="blue darken-1"
text
@@ -42,6 +53,10 @@ export default {
dataListSelection: { type: Object, default: null }
},
data: () => ({
//case 4745: GZCaseStatus values
STATUS_DONE: 5,
STATUS_NEVER: 6,
closeAs: 5,
jobActive: false,
opening: false,
currentJobId: null,
@@ -68,7 +83,7 @@ export default {
//Clear any possible prior errors
vm.$emit("ext-show-job-log", "clear");
vm.opening = open;
const url = `job-operations/batch-close-cases?open=${open}`;
const url = `job-operations/batch-close-cases?open=${open}&status=${this.closeAs}`;
try {
this.progress = "";
let jobId = await window.$gz.api.upsert(url, this.dataListSelection);

View File

@@ -118,7 +118,10 @@
@change="fieldValueChanged('agent')"
></v-checkbox>
</v-col>
<v-col v-if="obj.agent && obj.id != 0" cols="12">
<v-col
v-if="obj.agent && obj.id != 0 && !formState.dirty"
cols="12"
>
<v-card outlined class="pa-3" data-cy="agentTokens">
<div class="text-subtitle-1">
{{ $sock.t("AgentTokens") }}
@@ -136,6 +139,15 @@
>
<code>{{ newAgentToken }}</code>
</div>
<v-btn
small
class="mt-2"
data-cy="copyAgentToken"
@click="copyAgentToken()"
>
<v-icon small left>$sockiCopy</v-icon>
{{ $sock.t("CopyToClipboard") }}
</v-btn>
</v-alert>
<v-simple-table v-if="agentTokens.length > 0" dense>
<tbody>
@@ -151,6 +163,12 @@
<br />
{{ $sock.t("AgentTokenLastUsed") }}:
{{ tokenDate(t.lastUsed) }}
<br />
{{
t.readOnly
? $sock.t("AgentTokenReadOnly")
: $sock.t("AgentTokenCanChange")
}}
</td>
<td>
<span v-if="t.revoked" class="text-caption">
@@ -171,14 +189,14 @@
{{ $sock.t("AgentTokenNone") }}
</div>
<v-row v-if="rights.change" dense align="center">
<v-col cols="12" sm="6">
<v-col cols="12" sm="4">
<v-text-field
v-model="newAgentTokenName"
dense
:label="$sock.t('AgentTokenName')"
></v-text-field>
</v-col>
<v-col cols="6" sm="3">
<v-col cols="6" sm="2">
<v-text-field
v-model.number="newAgentTokenDays"
dense
@@ -187,7 +205,16 @@
:label="$sock.t('AgentTokenExpiresDays')"
></v-text-field>
</v-col>
<!-- case 4688 phase 2: off (read only) unless asked for -->
<v-col cols="6" sm="3">
<v-checkbox
v-model="newAgentTokenCanChange"
dense
:label="$sock.t('AgentTokenCanChange')"
data-cy="agentTokenCanChange"
></v-checkbox>
</v-col>
<v-col cols="12" sm="3">
<v-btn
small
color="primary"
@@ -198,7 +225,13 @@
</v-row>
</v-card>
</v-col>
<v-col v-if="obj.allowLogin" cols="12" sm="6" lg="4" xl="3">
<v-col
v-if="obj.allowLogin && !obj.agent"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-text-field
id="username"
ref="login"
@@ -217,7 +250,13 @@
></v-text-field>
</v-col>
<v-col v-if="obj.allowLogin" cols="12" sm="6" lg="4" xl="3">
<v-col
v-if="obj.allowLogin && !obj.agent"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-text-field
id="password"
ref="password"
@@ -561,6 +600,7 @@ export default {
newAgentToken: null,
newAgentTokenName: null,
newAgentTokenDays: 90,
newAgentTokenCanChange: false,
optionsObj: {
id: 0,
concurrency: 0,
@@ -962,6 +1002,13 @@ export default {
window.$gz.locale.getHour12()
);
},
copyAgentToken() {
window.$gz.util.copyToClipboard(this.newAgentToken);
window.$gz.eventBus.$emit(
"notify-success",
this.$sock.t("CopyToClipboard")
);
},
async getAgentTokens() {
const vm = this;
vm.newAgentToken = null;
@@ -984,7 +1031,8 @@ export default {
const res = await window.$gz.api.post("agent-token", {
userId: vm.obj.id,
name: vm.newAgentTokenName,
expiresDays: days
expiresDays: days,
canChange: vm.newAgentTokenCanChange
});
if (res.error) {
vm.formState.serverError = res.error;
@@ -994,6 +1042,7 @@ export default {
await vm.getAgentTokens();
vm.newAgentToken = res.data.token;
vm.newAgentTokenName = null;
vm.newAgentTokenCanChange = false;
},
async revokeAgentToken(t) {
const vm = this;
@@ -1276,6 +1325,9 @@ async function fetchTranslatedText() {
"AgentTokenRevoke",
"AgentTokenRevoked",
"AgentTokenNone",
"AgentTokenCanChange",
"AgentTokenReadOnly",
"CopyToClipboard",
"Vendor",
"User",
"UserSettings",

View File

@@ -18,6 +18,50 @@
@input="fieldValueChanged('name')"
></v-text-field>
</v-col>
<!-- case 4745: triage (Sockeye only, so plain English); a closing status closes the case when saved -->
<v-col cols="12" sm="6" lg="4" xl="3">
<v-select
ref="status"
v-model="obj.status"
dense
:items="selectLists.statuses"
item-text="name"
item-value="id"
:readonly="formState.readOnly"
:label="$sock.t('GZCaseStatus')"
data-cy="status"
:error-messages="form().serverErrors(this, 'status')"
@input="fieldValueChanged('status')"
></v-select>
</v-col>
<v-col cols="12" sm="6" lg="4" xl="3">
<v-select
ref="product"
v-model="obj.product"
dense
:items="selectLists.products"
item-text="name"
item-value="id"
:readonly="formState.readOnly"
:label="$sock.t('GZCaseProduct')"
data-cy="product"
:error-messages="form().serverErrors(this, 'product')"
@input="fieldValueChanged('product')"
></v-select>
</v-col>
<v-col cols="12" sm="6" lg="4" xl="3">
<v-text-field
ref="release"
v-model="obj.release"
dense
:readonly="formState.readOnly"
:label="$sock.t('GZCaseRelease')"
hint="e.g. 8.3.2; needed once the case is Built"
data-cy="release"
:error-messages="form().serverErrors(this, 'release')"
@input="fieldValueChanged('release')"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" lg="4" xl="3">
<gz-pick-list
ref="customerId"
@@ -152,7 +196,14 @@ export default {
closed: null,
customerId: null,
wiki: null,
tags: []
tags: [],
status: GZCASE_STATUS_NEW,
product: null,
release: null
},
selectLists: {
statuses: [],
products: []
},
formState: {
ready: false,
@@ -225,6 +276,9 @@ export default {
//case 4714: a duplicate is a new, open case: the server gives it the next case number when saved
this.obj.caseId = 0;
this.obj.closed = null;
//case 4745: and starts at the beginning of triage
this.obj.status = GZCASE_STATUS_NEW;
this.obj.release = null;
this.obj.created = window.$gz.locale.nowUTC8601String();
this.obj.name = `${this.obj.name} - ${window.$gz.translation.get(
"Copy"
@@ -275,7 +329,9 @@ export default {
},
async setClosed(close) {
//case 4604: Close sets the closed date to now, saves the case and goes back; Open clears it and saves
//case 4745: closing makes it Done, reopening New (Never and Duplicate are chosen in Status)
this.obj.closed = close ? window.$gz.locale.nowUTC8601String() : null;
this.obj.status = close ? GZCASE_STATUS_DONE : GZCASE_STATUS_NEW;
this.fieldValueChanged("closed");
await this.submit();
if (close && !this.formState.dirty) {
@@ -357,6 +413,11 @@ export default {
} else {
//PUT
vm.obj.concurrency = res.data.concurrency;
//case 4745: the server sets closed from the status (and may move the status), so show what it saved
//(the PUT answer carries these as well as concurrency)
vm.obj.closed = res.data.closed;
vm.obj.status = res.data.status;
vm.obj.release = res.data.release;
}
window.$gz.form.setFormState({
vm: vm,
@@ -602,12 +663,34 @@ function generateMenu(vm) {
let JUST_DELETED = false;
//case 4745: GZCaseStatus values the form sets itself
const GZCASE_STATUS_NEW = 1;
const GZCASE_STATUS_DONE = 5;
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText();
await window.$gz.formCustomTemplate.get(FORM_CUSTOM_TEMPLATE_KEY, vm);
await populateSelectionLists(vm);
}
//////////////////////
//
//
async function populateSelectionLists(vm) {
//case 4745: status in process order, not by name
await window.$gz.enums.fetchEnumList("gzcasestatus");
vm.selectLists.statuses = window.$gz.enums.getSelectionList(
"gzcasestatus",
true
);
await window.$gz.enums.fetchEnumList("gzcaseproduct");
vm.selectLists.products = window.$gz.enums.getSelectionList(
"gzcaseproduct",
true
);
}
//////////////////////////////////////////////////////////
@@ -620,6 +703,9 @@ async function fetchTranslatedText() {
"GZCaseName",
"GZCaseId",
"GZCaseClosed",
"GZCaseStatus",
"GZCaseProduct",
"GZCaseRelease",
"Created",
"GZCaseNotes",
"Customer"