feat(web): lien « Buy me a coffee » (nav, réglages, funding)
- constante BUYMEACOFFEE_URL ; item de nav « Offrez-moi un café » - section support dans les Réglages + i18n EN/FR - champ `funding` dans le package.json publié ; mentions README EN/FR Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -255,6 +255,12 @@ node packages/server/scripts/acceptance-p4.mjs # Web Push + commande WS `answe
|
|||||||
node packages/server/scripts/acceptance-p5.mjs # groupes de travail : CRUD + broadcast WS + CASCADE
|
node packages/server/scripts/acceptance-p5.mjs # groupes de travail : CRUD + broadcast WS + CASCADE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Soutenir le projet
|
||||||
|
|
||||||
|
Arboretum est un projet personnel libre et auto-financé. S'il vous fait gagner du temps, vous pouvez soutenir son développement :
|
||||||
|
|
||||||
|
[](https://buymeacoffee.com/johanleroy)
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
MIT — voir [LICENSE](LICENSE).
|
MIT — voir [LICENSE](LICENSE).
|
||||||
|
|||||||
@@ -255,6 +255,12 @@ node packages/server/scripts/acceptance-p4.mjs # Web Push + WS `answer` comman
|
|||||||
node packages/server/scripts/acceptance-p5.mjs # work groups: CRUD + WS broadcast + CASCADE
|
node packages/server/scripts/acceptance-p5.mjs # work groups: CRUD + WS broadcast + CASCADE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Arboretum is a free, self-funded side project. If it saves you time, you can support its development:
|
||||||
|
|
||||||
|
[](https://buymeacoffee.com/johanleroy)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT — see [LICENSE](LICENSE).
|
MIT — see [LICENSE](LICENSE).
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://git.lidge.fr/johanleroy/arboretum/issues"
|
"url": "https://git.lidge.fr/johanleroy/arboretum/issues"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "buymeacoffee",
|
||||||
|
"url": "https://buymeacoffee.com/johanleroy"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"git",
|
"git",
|
||||||
"worktree",
|
"worktree",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { computed, type Component } from 'vue';
|
import { computed, type Component } from 'vue';
|
||||||
import { useRoute, type RouteLocationRaw } from 'vue-router';
|
import { useRoute, type RouteLocationRaw } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Boxes, GitBranch, LifeBuoy, Settings, TerminalSquare } from '@lucide/vue';
|
import { Boxes, Coffee, GitBranch, LifeBuoy, Settings, TerminalSquare } from '@lucide/vue';
|
||||||
import { useSessionsStore } from '../stores/sessions';
|
import { useSessionsStore } from '../stores/sessions';
|
||||||
import GiteaIcon from '../components/ui/GiteaIcon.vue';
|
import GiteaIcon from '../components/ui/GiteaIcon.vue';
|
||||||
import { REPO_SOURCE_URL } from '../lib/constants';
|
import { BUYMEACOFFEE_URL, REPO_SOURCE_URL } from '../lib/constants';
|
||||||
|
|
||||||
export interface NavEntry {
|
export interface NavEntry {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -41,6 +41,7 @@ export function useNav() {
|
|||||||
{ key: 'settings', to: { name: 'settings' }, icon: Settings, label: t('nav.settings'), match: ['settings'], badge: 0 },
|
{ key: 'settings', to: { name: 'settings' }, icon: Settings, label: t('nav.settings'), match: ['settings'], badge: 0 },
|
||||||
{ key: 'help', to: { name: 'help' }, icon: LifeBuoy, label: t('nav.help'), match: ['help'], badge: 0 },
|
{ key: 'help', to: { name: 'help' }, icon: LifeBuoy, label: t('nav.help'), match: ['help'], badge: 0 },
|
||||||
{ key: 'gitea', href: REPO_SOURCE_URL, icon: GiteaIcon, label: t('nav.gitea'), match: [], badge: 0 },
|
{ key: 'gitea', href: REPO_SOURCE_URL, icon: GiteaIcon, label: t('nav.gitea'), match: [], badge: 0 },
|
||||||
|
{ key: 'buymeacoffee', href: BUYMEACOFFEE_URL, icon: Coffee, label: t('nav.buymeacoffee'), match: [], badge: 0 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isActive = (match: string[]): boolean => match.includes(String(route.name));
|
const isActive = (match: string[]): boolean => match.includes(String(route.name));
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ export default {
|
|||||||
help: 'Help',
|
help: 'Help',
|
||||||
more: 'More',
|
more: 'More',
|
||||||
gitea: 'Source code',
|
gitea: 'Source code',
|
||||||
|
buymeacoffee: 'Buy me a coffee',
|
||||||
waiting: 'waiting',
|
waiting: 'waiting',
|
||||||
},
|
},
|
||||||
controls: {
|
controls: {
|
||||||
@@ -364,6 +365,10 @@ export default {
|
|||||||
vapidContact: 'VAPID contact',
|
vapidContact: 'VAPID contact',
|
||||||
none: 'none',
|
none: 'none',
|
||||||
flagHint: 'CLI flag: {flag}',
|
flagHint: 'CLI flag: {flag}',
|
||||||
|
// Support
|
||||||
|
support: 'Support',
|
||||||
|
supportHint: 'Arboretum is a free, self-funded side project. If it saves you time, you can support its development.',
|
||||||
|
supportCta: 'Buy me a coffee',
|
||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
title: 'Help',
|
title: 'Help',
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ const fr: typeof en = {
|
|||||||
help: 'Aide',
|
help: 'Aide',
|
||||||
more: 'Plus',
|
more: 'Plus',
|
||||||
gitea: 'Code source',
|
gitea: 'Code source',
|
||||||
|
buymeacoffee: 'Offrez-moi un café',
|
||||||
waiting: 'en attente',
|
waiting: 'en attente',
|
||||||
},
|
},
|
||||||
controls: {
|
controls: {
|
||||||
@@ -367,6 +368,10 @@ const fr: typeof en = {
|
|||||||
vapidContact: 'Contact VAPID',
|
vapidContact: 'Contact VAPID',
|
||||||
none: 'aucune',
|
none: 'aucune',
|
||||||
flagHint: 'Flag CLI : {flag}',
|
flagHint: 'Flag CLI : {flag}',
|
||||||
|
// Soutien
|
||||||
|
support: 'Soutenir le projet',
|
||||||
|
supportHint: 'Arboretum est un projet personnel libre et auto-financé. S’il vous fait gagner du temps, vous pouvez soutenir son développement.',
|
||||||
|
supportCta: 'Offrez-moi un café',
|
||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
title: 'Aide',
|
title: 'Aide',
|
||||||
|
|||||||
@@ -6,3 +6,9 @@
|
|||||||
* tous les utilisateurs de la solution.
|
* tous les utilisateurs de la solution.
|
||||||
*/
|
*/
|
||||||
export const REPO_SOURCE_URL = 'https://git.lidge.fr/johanleroy/arboretum';
|
export const REPO_SOURCE_URL = 'https://git.lidge.fr/johanleroy/arboretum';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lien « Buy me a coffee » affiché dans la navigation et la page Réglages.
|
||||||
|
* En dur (pas un réglage) : page de don personnelle, identique pour tous.
|
||||||
|
*/
|
||||||
|
export const BUYMEACOFFEE_URL = 'https://buymeacoffee.com/johanleroy';
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
/* Tailwind v4 ne met plus cursor:pointer sur les boutons → on le rétablit
|
||||||
|
(sauf désactivés, qui gardent le curseur par défaut / not-allowed). */
|
||||||
|
button:not(:disabled),
|
||||||
|
[role='button']:not([aria-disabled='true']) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
/* Champs : rayon unifié + anneau de focus visible (absent auparavant). */
|
/* Champs : rayon unifié + anneau de focus visible (absent auparavant). */
|
||||||
.input {
|
.input {
|
||||||
|
|||||||
@@ -178,13 +178,31 @@
|
|||||||
</dl>
|
</dl>
|
||||||
<SkeletonRow v-else />
|
<SkeletonRow v-else />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Support -->
|
||||||
|
<section class="card flex flex-col gap-3">
|
||||||
|
<h2 class="flex items-center gap-2 text-sm font-semibold text-zinc-100">
|
||||||
|
<Coffee :size="16" /> {{ t('settings.support') }}
|
||||||
|
</h2>
|
||||||
|
<p class="text-xs text-zinc-500">{{ t('settings.supportHint') }}</p>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
:href="BUYMEACOFFEE_URL"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="inline-flex h-9 items-center justify-center gap-1.5 rounded-lg border border-zinc-700 bg-zinc-800 px-3 text-sm font-medium text-zinc-200 transition-colors hover:bg-zinc-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/70 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950"
|
||||||
|
>
|
||||||
|
<Coffee :size="16" /> {{ t('settings.supportCta') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Bell, BellOff, Check, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Trash2, X } from '@lucide/vue';
|
import { Bell, BellOff, Check, Coffee, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Trash2, X } from '@lucide/vue';
|
||||||
import type {
|
import type {
|
||||||
AuditLogEntry,
|
AuditLogEntry,
|
||||||
AuditLogsResponse,
|
AuditLogsResponse,
|
||||||
@@ -195,6 +213,7 @@ import type {
|
|||||||
TokensListResponse,
|
TokensListResponse,
|
||||||
} from '@arboretum/shared';
|
} from '@arboretum/shared';
|
||||||
import { api, ApiError } from '../lib/api';
|
import { api, ApiError } from '../lib/api';
|
||||||
|
import { BUYMEACOFFEE_URL } from '../lib/constants';
|
||||||
import { useSettingsStore } from '../stores/settings';
|
import { useSettingsStore } from '../stores/settings';
|
||||||
import { usePushStore } from '../stores/push';
|
import { usePushStore } from '../stores/push';
|
||||||
import { useToastsStore } from '../stores/toasts';
|
import { useToastsStore } from '../stores/toasts';
|
||||||
|
|||||||
Reference in New Issue
Block a user