feat(web): IDE unique comme seule coquille, ancien dashboard retiré
L'IDE (/ide) devient l'unique interface sur tous les supports (navigateur, PWA, app Electron chargent la même SPA). Tout le cycle de vie de l'ancien monde est réintégré dans l'IDE ; AppShell et les vues Dashboard / Sessions / Groups / GroupView / SessionView sont supprimés. - Infra: pile de modals Teleport (stores/modals.ts + ModalHost) et menu contextuel (useContextMenu + ContextMenu), montés dans IdeShell sans le démonter (attaches WS du dock préservées). - Explorer: créer worktree (branche + baseRef + session), travailler sur main, prune, promote (escalade force), supprimer worktree, commit/push, masquer/ supprimer repo, ajouter dépôt local ou cloner. - Sessions: create/kill/resume/fork, hide/unhide, archive/unarchive, masquer les découvertes, supervision AttentionList + réponse aux dialogues. - Groupes: create/delete, composition, lancement de session de groupe (ouvre le terminal dans le dock). - Compte: Réglages et Aide en overlays par-dessus l'IDE ; langue, push/PWA, tokens, connexions git, RGPD, déconnexion. Command Palette et deep-links recâblés pour rester dans l'IDE. - Routes: les anciens chemins rendent IdeShell via meta (overlay/panel/ terminal) ; /workspace/:repoId/:wt inchangé (contrat extension VS Code). Mobile: nav à 6 panneaux (explorer/editor/terminal/git/sessions/groups). La grille multi-terminaux de groupe est retirée (supplantée par la session de groupe unique P6 via --add-dir + le dock multi-onglets).
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
import { useListControls, type FilterDef, type SortDef } from '../src/composables/useListControls';
|
||||
|
||||
interface Item {
|
||||
n: number;
|
||||
}
|
||||
|
||||
const sorts: SortDef<Item>[] = [
|
||||
{ key: 'n', labelKey: 'n', compare: (a, b) => a.n - b.n, defaultDir: 'asc' },
|
||||
];
|
||||
const filters: FilterDef<Item>[] = [
|
||||
{
|
||||
key: 'parity',
|
||||
labelKey: 'parity',
|
||||
options: [
|
||||
{ value: 'even', label: 'even' },
|
||||
{ value: 'odd', label: 'odd' },
|
||||
],
|
||||
predicate: (it, active) =>
|
||||
(active.has('even') && it.n % 2 === 0) || (active.has('odd') && it.n % 2 === 1),
|
||||
},
|
||||
];
|
||||
|
||||
function make() {
|
||||
const source = ref<Item[]>([{ n: 5 }, { n: 2 }, { n: 4 }, { n: 1 }, { n: 3 }]);
|
||||
const c = useListControls<Item>({ source: () => source.value, sorts, filters, defaultPageSize: 2 });
|
||||
return { source, c };
|
||||
}
|
||||
|
||||
describe('useListControls', () => {
|
||||
it('trie en ascendant par défaut', () => {
|
||||
const { c } = make();
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([1, 2]); // page 1, taille 2
|
||||
expect(c.total.value).toBe(5);
|
||||
expect(c.pageCount.value).toBe(3);
|
||||
});
|
||||
|
||||
it('inverse la direction au re-clic de la même clé', () => {
|
||||
const { c } = make();
|
||||
c.setSort('n'); // même clé → desc
|
||||
expect(c.sortDir.value).toBe('desc');
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([5, 4]);
|
||||
});
|
||||
|
||||
it('pagine et borne la page', () => {
|
||||
const { c } = make();
|
||||
c.setPage(3);
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([5]);
|
||||
c.setPage(99);
|
||||
expect(c.page.value).toBe(3); // borné à pageCount
|
||||
});
|
||||
|
||||
it('filtre (OR intra-filtre) et revient page 1', () => {
|
||||
const { c } = make();
|
||||
c.setPage(2);
|
||||
c.toggleFilter('parity', 'even');
|
||||
expect(c.page.value).toBe(1);
|
||||
expect(c.total.value).toBe(2); // 2 et 4
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([2, 4]);
|
||||
});
|
||||
|
||||
it('setSearch revient page 1, mais écrire search.value directement (hydratation) ne touche pas la page', () => {
|
||||
const { c } = make();
|
||||
c.setPage(3);
|
||||
// hydratation URL : on écrit le ref directement → la page fournie par l'URL est préservée
|
||||
c.search.value = '';
|
||||
expect(c.page.value).toBe(3);
|
||||
// saisie utilisateur via setSearch → retour page 1
|
||||
c.setSearch('x');
|
||||
expect(c.page.value).toBe(1);
|
||||
});
|
||||
|
||||
it('pageSize 0 affiche tout', () => {
|
||||
const { c } = make();
|
||||
c.setPageSize(0);
|
||||
expect(c.pageCount.value).toBe(1);
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
it('reste réactif aux mutations de la source (upsert temps réel)', () => {
|
||||
const { source, c } = make();
|
||||
source.value = [...source.value, { n: 0 }];
|
||||
expect(c.total.value).toBe(6);
|
||||
expect(c.processed.value.map((i) => i.n)).toEqual([0, 1]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user