managarten/packages/feedback/src/FeedbackForm.svelte
Till JS ab24db36dd fix(packages): cross-package broken imports + missing exports
Five unrelated packages each had a few imports pointing at the wrong
file or missing from their public surface. Grouped because none of
the individual fixes warrants its own commit and they all unblock
the same downstream consumer (apps/mana/apps/web type-check).

packages/help
  - HelpPage.svelte: `'../types.js'` and `'./content'` for
    HelpPageProps/HelpSection/SearchResult — neither path exists.
    Real homes are `../ui-types` (props) and `../search-types`
    (search shapes). Fix the imports.
  - HelpSearch.svelte: same `'../content'` typo for SearchResult →
    `'../search-types'`.
  - translations.ts: `'./types.js'` for HelpPageTranslations →
    `'./ui-types'`.
  - ui-types.ts: was importing SearchResult from `'./content'` but
    that module only exports content shapes. Split into two imports
    so HelpContent stays from content.ts and SearchResult comes from
    search-types.ts.

packages/feedback
  - FeedbackPage.svelte: imported `Feedback` and `CreateFeedbackInput`
    from `'./createFeedbackService'` but the service module only
    exports the service factory. Real homes are `'./feedback'`
    (Feedback) and `'./api'` (CreateFeedbackInput).
  - FeedbackForm.svelte: same `'./feedback'` typo for
    CreateFeedbackInput → `'./api'`.

packages/subscriptions
  - UsageCard / CostCard / pages/SubscriptionPage: all imported
    UsageData / CostItem from `'./plans'` but those types live in
    `'./usage'`. SubscriptionPage additionally had a relative-path
    bug — it's at `src/pages/`, not `src/`, so `./plans` resolved
    to `pages/plans` (nonexistent). Now imports `'../plans'` for
    plan types and `'../usage'` for usage/cost types.

packages/shared-ui
  - index.ts: re-exports the QuickInputItem family from
    `./quick-input` but had forgotten `HighlightPattern`. Added.
    Apps that build their own InputBar pattern config (e.g.
    mana/web/src/lib/quick-input/types.ts) need it as a public type.
  - PillNavigation.svelte: imported `SpotlightAction` and
    `ContentSearcher` from `./GlobalSpotlight.svelte` (a Svelte
    component file), which only re-exports the default. Both types
    live in `./types`. Move them to the existing types-import
    block; the GlobalSpotlight import becomes a plain default.

packages/shared-auth-ui
  - stores/createAuthStore.svelte.ts: imported AuthServiceAdapter /
    AuthResult / BaseUser from `'./types'` (nonexistent — the file
    is `'./store-types'`).

Net: -23 type errors. Zero behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:23:34 +02:00

191 lines
4.1 KiB
Svelte

<script lang="ts">
import type { CreateFeedbackInput } from './api';
interface Props {
onSubmit: (input: CreateFeedbackInput) => Promise<void>;
onCancel?: () => void;
isSubmitting?: boolean;
feedbackLabel?: string;
submitLabel?: string;
cancelLabel?: string;
feedbackPlaceholder?: string;
}
let {
onSubmit,
onCancel,
isSubmitting = false,
feedbackLabel = 'Dein Feedback',
submitLabel = 'Feedback senden',
cancelLabel = 'Abbrechen',
feedbackPlaceholder = 'Was gefällt dir? Was können wir verbessern?',
}: Props = $props();
let feedbackText = $state('');
let error = $state('');
async function handleSubmit(e: Event) {
e.preventDefault();
error = '';
if (feedbackText.trim().length < 10) {
error = 'Bitte gib mindestens 10 Zeichen ein.';
return;
}
try {
await onSubmit({
feedbackText: feedbackText.trim(),
});
// Reset form on success
feedbackText = '';
} catch (err) {
error = err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten.';
}
}
</script>
<form class="feedback-form" onsubmit={handleSubmit}>
<div class="feedback-form__field">
<label for="feedback-text" class="feedback-form__label">{feedbackLabel}</label>
<textarea
id="feedback-text"
class="feedback-form__textarea"
placeholder={feedbackPlaceholder}
bind:value={feedbackText}
rows="5"
maxlength="2000"
disabled={isSubmitting}
required
></textarea>
<span class="feedback-form__counter">{feedbackText.length}/2000</span>
</div>
{#if error}
<div class="feedback-form__error">{error}</div>
{/if}
<div class="feedback-form__actions">
{#if onCancel}
<button
type="button"
class="feedback-form__button feedback-form__button--secondary"
onclick={onCancel}
disabled={isSubmitting}
>
{cancelLabel}
</button>
{/if}
<button
type="submit"
class="feedback-form__button feedback-form__button--primary"
disabled={isSubmitting || feedbackText.trim().length < 10}
>
{#if isSubmitting}
Wird gesendet...
{:else}
{submitLabel}
{/if}
</button>
</div>
</form>
<style>
.feedback-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.feedback-form__field {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
.feedback-form__label {
font-size: 0.875rem;
font-weight: 500;
color: hsl(var(--color-foreground, 0 0% 17%));
}
.feedback-form__textarea {
padding: 0.75rem;
border: 1px solid hsl(var(--color-border, 0 0% 90%));
border-radius: 0.5rem;
font-size: 0.875rem;
color: hsl(var(--color-foreground, 0 0% 17%));
background: hsl(var(--color-input, 0 0% 100%));
transition: border-color 0.2s ease;
}
.feedback-form__textarea::placeholder {
color: hsl(var(--color-muted-foreground, 0 0% 40%));
}
.feedback-form__textarea:focus {
outline: none;
border-color: hsl(var(--color-primary, 47 95% 58%));
}
.feedback-form__textarea {
resize: vertical;
min-height: 120px;
}
.feedback-form__counter {
align-self: flex-end;
font-size: 0.75rem;
color: hsl(var(--color-muted-foreground, 0 0% 40%));
}
.feedback-form__error {
padding: 0.75rem;
border-radius: 0.5rem;
background: hsl(var(--color-error, 6 78% 57%) / 0.1);
color: hsl(var(--color-error, 6 78% 57%));
font-size: 0.875rem;
}
.feedback-form__actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
margin-top: 0.5rem;
}
.feedback-form__button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.5rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.feedback-form__button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.feedback-form__button--primary {
background: hsl(var(--color-primary, 47 95% 58%));
color: hsl(var(--color-primary-foreground, 0 0% 0%));
}
.feedback-form__button--primary:hover:not(:disabled) {
background: hsl(var(--color-primary, 47 95% 58%) / 0.9);
}
.feedback-form__button--secondary {
background: transparent;
border: 1px solid hsl(var(--color-border, 0 0% 90%));
color: hsl(var(--color-foreground, 0 0% 17%));
}
.feedback-form__button--secondary:hover:not(:disabled) {
background: hsl(var(--color-muted, 0 0% 90%));
}
</style>