Skip to content

Feature/pcd activity sync#873

Open
Zeeshan138063 wants to merge 4 commits into
ActivityWatch:masterfrom
Zeeshan138063:feature/pcd-activity-sync
Open

Feature/pcd activity sync#873
Zeeshan138063 wants to merge 4 commits into
ActivityWatch:masterfrom
Zeeshan138063:feature/pcd-activity-sync

Conversation

@Zeeshan138063

Copy link
Copy Markdown

No description provided.

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a "PCD Activity Sync" admin feature into ActivityWatch's web UI, adding a dedicated /pcd-admin full-page view (PcdAdmin.vue) and an inline PCDAdminPanel component dropped directly onto the Home page. Both surfaces allow an admin to authenticate against a local PCD API, manage a registered email address, configure a remote server URL, and view sync logs.

  • Two overlapping implementations with incompatible auth: PCDAdminPanel sends {username, password} to /api/0/pcd/verify, while PcdAdmin.vue sends only {password} — only one can be correct for the backend.
  • Empty-email bug in PCDAdminPanel: emailState returns null for an empty field, but guards check === false, allowing a blank email to be submitted.
  • Organisation-specific internals in an open-source repo: Hard-coded prescribingcaredirect.co.uk hostnames (prod/QA/dev) are committed to the public ActivityWatch repository, and the PCDAdminPanel component is injected into the community Home view seen by all ActivityWatch users.

Confidence Score: 2/5

Not safe to merge into the open-source ActivityWatch master branch in its current state.

The two admin components call the same /api/0/pcd/verify endpoint with different request bodies ({username, password} vs {password}), so at most one will authenticate successfully. An empty-email submission is also possible through the panel's guard logic. More fundamentally, the PR embeds an organisation-specific tool (with hard-coded prescribingcaredirect.co.uk hostnames) directly into the Home page and header of an open-source project used by a broad community.

src/components/PCDAdminPanel.vue has the auth mismatch and the empty-email guard bug; src/views/PcdAdmin.vue contains the hard-coded internal URLs; src/views/Home.vue injects the organisation panel into the shared community home screen.

Important Files Changed

Filename Overview
src/components/PCDAdminPanel.vue New compact admin panel with two bugs: empty-email guard uses === false instead of !== true (allows submitting blank email), and auth payload sends {username, password} — inconsistent with PcdAdmin.vue which sends only {password} to the same endpoint.
src/views/PcdAdmin.vue New full-page admin view; hard-codes production/QA/dev hostnames for prescribingcaredirect.co.uk — internal infrastructure URLs that should not be committed to an open-source repository.
src/views/Home.vue Embeds PCDAdminPanel (an organisation-specific internal tool) into the community Home page that every ActivityWatch user sees; panel should only live on the dedicated /pcd-admin route.
src/components/Header.vue Adds a "PCD Admin" nav link pointing to /pcd-admin and imports the user-shield icon; straightforward change with no logic issues.
src/route.js Registers /pcd-admin route with lazy-loaded PcdAdmin view; minimal and correct change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User
    participant H as Home.vue (PCDAdminPanel)
    participant P as PcdAdmin.vue (/pcd-admin)
    participant API as /api/0/pcd

    U->>H: Click "PCD" trigger (Home page)
    H->>API: "POST /verify {username, password}"
    API-->>H: "{token}"
    H->>API: GET /email (X-PCD-Admin-Token)
    API-->>H: "{email}"
    H->>API: "PUT /email {new_email}"
    API-->>H: "{ok}"

    U->>P: Navigate to /pcd-admin
    P->>API: "POST /verify {password}"
    API-->>P: "{token}"
    P->>API: GET /config
    P->>API: GET /email
    P->>API: GET /logs
    API-->>P: config / email / logs
    P->>API: "PUT /config {base_url}"
    P->>API: "PUT /email {new_email}"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User
    participant H as Home.vue (PCDAdminPanel)
    participant P as PcdAdmin.vue (/pcd-admin)
    participant API as /api/0/pcd

    U->>H: Click "PCD" trigger (Home page)
    H->>API: "POST /verify {username, password}"
    API-->>H: "{token}"
    H->>API: GET /email (X-PCD-Admin-Token)
    API-->>H: "{email}"
    H->>API: "PUT /email {new_email}"
    API-->>H: "{ok}"

    U->>P: Navigate to /pcd-admin
    P->>API: "POST /verify {password}"
    API-->>P: "{token}"
    P->>API: GET /config
    P->>API: GET /email
    P->>API: GET /logs
    API-->>P: config / email / logs
    P->>API: "PUT /config {base_url}"
    P->>API: "PUT /email {new_email}"
Loading

Reviews (1): Last reviewed commit: "feat(pcd): add PCDAdminPanel quick-acces..." | Re-trigger Greptile

Comment on lines +88 to +113
async verifyAdmin() {
if (!this.username || !this.password) return;
this.loading = true;
this.authError = '';
try {
const res = await fetch(`${API_BASE}/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: this.username, password: this.password }),
});
if (res.ok) {
const data = await res.json();
this.sessionToken = data.token;
this.authenticated = true;
await this.loadEmail();
} else {
const data = await res.json().catch(() => ({}));
this.authError = data.error || 'Invalid credentials.';
this.password = '';
}
} catch {
this.authError = 'Could not reach the local server.';
} finally {
this.loading = false;
}
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Inconsistent auth payload vs. PcdAdmin.vue

PCDAdminPanel.vue calls /api/0/pcd/verify with { username, password } (lines 93–96), while PcdAdmin.vue calls the same endpoint with only { password } (line 140). These two components cannot both work correctly against the same backend endpoint. Whichever payload the server actually expects will cause the other component's login to silently fail or error.

Comment on lines +128 to +129
async saveEmail() {
if (!this.emailChanged || this.emailState === false) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Empty email can bypass the save guard

When the user clears the email field, emailState returns null (not false), so the this.emailState === false guard does not block the call. A PUT request with { new_email: '' } is then sent to the backend. The fix is to treat null as an invalid state in the guard, and also disable the Save button when emailState is null.

Suggested change
async saveEmail() {
if (!this.emailChanged || this.emailState === false) return;
async saveEmail() {
if (!this.emailChanged || this.emailState !== true) return;

variant="outline-primary"
size="sm"
@click="saveEmail"
:disabled="saving || !emailChanged || emailState === false"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Save button not disabled for an empty email

The button disables on emailState === false, but emailState returns null (not false) when the field is empty. A user who clears the registered email will see the Save button enabled and be able to submit an empty address.

Suggested change
:disabled="saving || !emailChanged || emailState === false"
:disabled="saving || !emailChanged || emailState !== true"

Comment thread src/views/PcdAdmin.vue
Comment on lines +82 to +87
const ENV_URLS: Record<string, string> = {
local: 'http://127.0.0.1:8005',
dev: 'https://api.dev.prescribingcaredirect.co.uk',
qa: 'https://api.qa.prescribingcaredirect.co.uk',
prod: 'https://api.prescribingcaredirect.co.uk',
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Organization-specific internal URLs hard-coded in an open-source repo

The ENV_URLS map embeds prescribingcaredirect.co.uk production, QA, and dev API hostnames in a public ActivityWatch repository. This permanently exposes internal infrastructure topology in git history and is inappropriate for a community open-source project. These should be driven by environment variables, a build-time config, or removed entirely before landing in the upstream codebase.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread src/views/Home.vue
i
| You can change which page opens when you open ActivityWatch (instead of this page) in the #[router-link(to="/settings") settings].

PCDAdminPanel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Organization-specific panel embedded in the community Home page

PCDAdminPanel is now injected into the Home view that every ActivityWatch user sees. This panel is an internal admin tool for a specific organisation (PCD) and has no business living on the welcome screen of an open-source project used by thousands of unrelated users. Even with the low-opacity "PCD" trigger, the component ships its endpoint URLs and auth logic globally. This should be restricted to the /pcd-admin dedicated route rather than embedded here.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant