+
Date Range
-
-
(open = !open)}
- >
- {displayLabel}
-
-
-
-
-
- {#if !isDefault}
-
- ✕
-
- {/if}
-
-
- {#if open}
+
- {/if}
+
+
+
+
+
+ {#each INTERVALS as interval}
+
+ {#snippet children({ checked })}
+
+ {interval.label}
+ {/snippet}
+
+ {/each}
+
+
+
+
+
+
+
diff --git a/app/javascript/pages/Home/signedIn/MultiSelect.svelte b/app/javascript/pages/Home/signedIn/MultiSelect.svelte
index 5e52053..bed8968 100644
--- a/app/javascript/pages/Home/signedIn/MultiSelect.svelte
+++ b/app/javascript/pages/Home/signedIn/MultiSelect.svelte
@@ -1,4 +1,5 @@
-
+
{label}
-
-
(open = !open)}
- >
-
- {displayText}
-
-
-
-
-
-
- {#if selected.length > 0}
-
- ×
-
- {/if}
-
-
- {#if open}
+
-
-
-
- {#each filtered as value}
-
+ {#snippet child({ props })}
+
- toggle(value)}
- class="mr-3 mb-0 h-4 w-4 min-w-4 appearance-none border border-surface-200 rounded bg-dark relative cursor-pointer p-0 checked:bg-primary checked:border-primary hover:border-surface-300 transition-colors duration-150"
- />
- {value}
-
- {/each}
+
+ {displayText}
+
+
+
+
+
+ {/snippet}
+
- {#if filtered.length === 0}
-
No results
- {/if}
-
+ {#if selected.length > 0}
+
+ ×
+
+ {/if}
- {/if}
+
+
+
+
+
+
+
onchange(next as string[])}
+ class="flex flex-col"
+ >
+ {#each filtered as value}
+
+ {#snippet children({ checked })}
+
+ ✓
+
+ {value}
+ {/snippet}
+
+ {/each}
+
+
+ {#if filtered.length === 0}
+
No results
+ {/if}
+
+
+
+
diff --git a/app/javascript/pages/Projects/Index.svelte b/app/javascript/pages/Projects/Index.svelte
new file mode 100644
index 0000000..4424a91
--- /dev/null
+++ b/app/javascript/pages/Projects/Index.svelte
@@ -0,0 +1,584 @@
+
+
+
+ {page_title}
+
+
+
+
+
+
My Projects
+ {#if archived_count > 0}
+
+
+ Active
+
+
+ Archived
+
+
+ {/if}
+
+
+
+ {#if !github_connected}
+
+
+ Heads up! You can't link projects to GitHub until you connect your
+ account.
+
+
+
+ Sign in with GitHub
+
+
+ Open settings
+
+
+
+ {/if}
+
+
+
+
+
+ {#if projects_data}
+
+
+ {#if projects_data.has_activity}
+ You've spent
+ {projects_data.total_time_label}
+ coding across {show_archived ? "archived" : "active"} projects.
+ {:else}
+ You haven't logged any time for this interval yet.
+ {/if}
+
+
+ {#if projects_data.projects.length == 0}
+
+
+ {show_archived
+ ? "No archived projects match this filter."
+ : "No active projects match this filter."}
+
+
+ {:else}
+
+ {#each projects_data.projects as project (project.id)}
+
+
+
+
+ {project.name}
+
+ {#if project.repository?.stars}
+
+
+
+
+ {project.repository.stars}
+
+ {/if}
+
+
+
+ {#if project.repository?.homepage}
+
+
+
+
+
+ {/if}
+ {#if project.repo_url}
+
+
+
+
+
+ {/if}
+ {#if project.manage_enabled}
+
openMappingEditor(project)}
+ >
+
+
+
+
+
+ {/if}
+ {#if show_archived && project.unarchive_path}
+
openStatusChangeModal(project, true)}
+ >
+
+
+
+
+
+ {:else if !show_archived && project.archive_path}
+
openStatusChangeModal(project, false)}
+ >
+
+
+
+
+
+
+ {/if}
+
+
+
+
+ {project.duration_label}
+
+
+ {#if project.repository?.description}
+
+ {project.repository.description}
+
+ {/if}
+
+ {#if project.repository?.formatted_languages}
+
+
+
+
+ {project.repository.formatted_languages}
+
+ {/if}
+
+ {#if project.repository?.last_commit_ago}
+
+
+
+
+ Last commit {project.repository.last_commit_ago}
+
+ {/if}
+
+ {#if project.broken_name}
+
+
+ Your editor may be sending invalid project names. Time is
+ shown here but can't be submitted to Hack Club programs.
+
+
+ {/if}
+
+ {#if project.manage_enabled && editingProjectKey === project.project_key && project.update_path}
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
+ {:else}
+
+
+
+ {#each Array.from({ length: skeletonCount }) as _unused, index (index)}
+
+ {/each}
+
+
+ {/if}
+
+
+
+ {#snippet actions()}
+ {#if pendingStatusAction}
+
+
+ Cancel
+
+
+
+ {/if}
+ {/snippet}
+
diff --git a/app/javascript/pages/Users/Settings/Access.svelte b/app/javascript/pages/Users/Settings/Access.svelte
index 7d2d059..3851bbe 100644
--- a/app/javascript/pages/Users/Settings/Access.svelte
+++ b/app/javascript/pages/Users/Settings/Access.svelte
@@ -1,6 +1,8 @@
-
{
+ const target = event.currentTarget as HTMLInputElement;
+ selectedFile = target.files?.[0] ?? null;
+ }}
+ class="w-full rounded-md border border-surface-200 bg-surface px-3 py-2 text-sm text-surface-content disabled:cursor-not-allowed disabled:opacity-60"
/>
-
- Import file
+
+ {#if submitError}
+ {submitError}
+ {/if}
+
+
+ {#if isStartingImport}
+ Starting import...
+ {:else if importInProgress}
+ Importing...
+ {:else}
+ Import file
+ {/if}
+
+ {#if importState !== "idle"}
+
+
+
+ Status: {importState}
+
+
+ {Math.round($tweenedProgress)}%
+
+
+
+
+ {formatCount(processedCount)} / {formatCount(totalCount)} processed
+
+ {#if importMessage}
+
{importMessage}
+ {/if}
+ {#if importState === "completed"}
+
+ Imported: {formatCount(importedCount)}. Skipped {formatCount(
+ skippedCount,
+ )} duplicates and {errorsCount.toLocaleString()} errors
+
+ {/if}
+
+ {/if}
{/if}
{/if}
diff --git a/app/javascript/pages/Users/Settings/Integrations.svelte b/app/javascript/pages/Users/Settings/Integrations.svelte
index 41d2160..aec1d47 100644
--- a/app/javascript/pages/Users/Settings/Integrations.svelte
+++ b/app/javascript/pages/Users/Settings/Integrations.svelte
@@ -1,6 +1,8 @@
Country
-
- Select a country
- {#each options.countries as country}
- {country.label}
- {/each}
-
+ items={[
+ { value: "", label: "Select a country" },
+ ...options.countries,
+ ]}
+ />
Timezone
-
- {#each options.timezones as timezone}
- {timezone.label}
- {/each}
-
+ items={options.timezones}
+ />
Save region settings
@@ -154,13 +147,16 @@
name="user[allow_public_stats_lookup]"
value="0"
/>
-
+ class="inline-flex h-4 w-4 min-w-4 items-center justify-center rounded border border-surface-200 bg-darker text-on-primary transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary"
+ >
+ {#snippet children({ checked })}
+
✓
+ {/snippet}
+
Allow public stats lookup
@@ -177,82 +173,77 @@
-
+
{#each options.themes as theme}
- {@const isSelected = selectedTheme === theme.value}
-
- (selectedTheme = theme.value)}
- />
-
-
-
-
- {theme.label}
-
-
{theme.description}
+ {#snippet children({ checked })}
+
+
+
+ {theme.label}
+
+
{theme.description}
+
+ {#if checked}
+
+ Selected
+
+ {/if}
- {#if isSelected}
-
- Selected
-
- {/if}
-
-
- Dashboard
- 2h 14m
-
+
+ Dashboard
+ 2h 14m
+
-
-
-
-
+
+
+
+
-
-
-
+ {/snippet}
+
{/each}
-
+
Save theme
diff --git a/app/javascript/pages/Users/Settings/types.ts b/app/javascript/pages/Users/Settings/types.ts
index ee8a0c4..79aeceb 100644
--- a/app/javascript/pages/Users/Settings/types.ts
+++ b/app/javascript/pages/Users/Settings/types.ts
@@ -60,7 +60,7 @@ export type PathsProps = {
migrate_heartbeats_path: string;
export_all_heartbeats_path: string;
export_range_heartbeats_path: string;
- import_heartbeats_path: string;
+ create_heartbeat_import_path: string;
create_deletion_path: string;
user_wakatime_mirrors_path: string;
};
@@ -138,6 +138,26 @@ export type UiProps = {
show_dev_import: boolean;
};
+export type HeartbeatImportStatusProps = {
+ import_id: string;
+ state: string;
+ progress_percent: number;
+ processed_count: number;
+ total_count: number | null;
+ imported_count: number | null;
+ skipped_count: number | null;
+ errors_count: number;
+ message: string;
+ updated_at: string;
+ started_at?: string;
+ finished_at?: string;
+};
+
+export type HeartbeatImportProps = {
+ import_id?: string | null;
+ status?: HeartbeatImportStatusProps | null;
+};
+
export type ErrorsProps = {
full_messages: string[];
username: string[];
@@ -189,6 +209,7 @@ export type DataPageProps = SettingsCommonProps & {
migration: MigrationProps;
data_export: DataExportProps;
ui: UiProps;
+ heartbeat_import: HeartbeatImportProps;
};
export type AdminPageProps = SettingsCommonProps & {
diff --git a/app/javascript/pages/WakatimeSetup/Step4.svelte b/app/javascript/pages/WakatimeSetup/Step4.svelte
index cee42be..6febe48 100644
--- a/app/javascript/pages/WakatimeSetup/Step4.svelte
+++ b/app/javascript/pages/WakatimeSetup/Step4.svelte
@@ -1,4 +1,5 @@
-
+
@@ -249,6 +250,6 @@
- <%= render 'shared/modal', modal_id: 'logout-modal', title: 'Woah hold on a sec', description: 'You sure you want to log out? You can sign back in later but that is a bit of a hassle...', icon_svg: '