소스 검색

Merge branch 'editTree' of pivozavri/econom_app into master

Всеволод Левитан 1 개월 전
부모
커밋
62b29d9d8d

+ 8 - 5
src/app.css

@@ -3,14 +3,15 @@
   line-height: 1.5;
   font-weight: 400;
 
-  color-scheme: light dark;
-  color: rgba(255, 255, 255, 0.87);
-  background-color: #242424;
-
+  color-scheme: light;
+  
   font-synthesis: none;
   text-rendering: optimizeLegibility;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
+  
+  color: #213547;
+  background-color: #f1f5f9;
 }
 
 a {
@@ -65,6 +66,7 @@ button:focus-visible {
   outline: 4px auto -webkit-focus-ring-color;
 }
 
+/* Светлая тема */
 @media (prefers-color-scheme: light) {
   :root {
     color: #213547;
@@ -75,5 +77,6 @@ button:focus-visible {
   }
   button {
     background-color: #f9f9f9;
+    color: #213547;
   }
-}
+}

+ 6 - 4
src/lib/components/ProgressBar.svelte

@@ -19,7 +19,7 @@
 <style>
     .progress-container {
         height: 8px;
-        background: #eee;
+        background: #e2e8f0;
         border-radius: 4px;
         overflow: hidden;
         margin-bottom: 2rem;
@@ -32,18 +32,20 @@
     .steps {
         display: flex;
         justify-content: space-between;
+        margin-bottom: 1rem;
     }
     .step-dot {
         width: 12px;
         height: 12px;
-        background: #ddd;
+        background: #cbd5e1;
         border-radius: 50%;
+        transition: all 0.2s;
     }
     .step-dot.active {
         background: #2563eb;
     }
     .step-dot.current {
         background: #1d4ed8;
-        transform: scale(1.2);
+        transform: scale(1.3);
     }
-</style>
+</style>

+ 140 - 38
src/lib/components/ResultCard.svelte

@@ -1,45 +1,51 @@
 <script>
-    import { fade } from "svelte/transition";
-
     let { result, reset } = $props();
 </script>
 
-<div class="result-card" transition:fade>
-    <h2>Рекомендуем: <strong>{result.recommendation}</strong></h2>
-    <p>{result.description}</p>
-
-    <div class="section">
-        <h4>Налоговый режим:</h4>
-        <ul>
-            {#each result.taxes as tax}
-                <li>
-                    <strong>{tax.name}</strong>: {tax.rate} →
-                    <em>{tax.note}</em>
-                </li>
-            {/each}
-        </ul>
-    </div>
-
-    <div class="pros-cons">
-        <div>
-            <h4>Плюсы</h4>
-            <ul class="pros">
-                {#each result.pros as pro}<li>{pro}</li>{/each}
-            </ul>
-        </div>
-        <div>
-            <h4>Минусы</h4>
-            <ul class="cons">
-                {#each result.cons as con}<li>{con}</li>{/each}
+<div class="result-card">
+    {#if result}
+        <h2>Рекомендуем: <strong>{result.recommendation}</strong></h2>
+        <p>{result.description}</p>
+
+        <div class="section">
+            <h4>Налоговый режим:</h4>
+            <ul>
+                {#each result.taxes as tax}
+                    <li>
+                        <strong>{tax.name}</strong>: {tax.rate} →
+                        <em>{tax.note}</em>
+                    </li>
+                {/each}
             </ul>
         </div>
-    </div>
 
-    {#if result.warning}
-        <div class="warning">
-            <strong>Внимание:</strong>
-            {result.warning}
+        <div class="pros-cons">
+            <div>
+                <h4>Плюсы</h4>
+                <ul class="pros">
+                    {#each result.pros as pro}<li>{pro}</li>{/each}
+                </ul>
+            </div>
+            <div>
+                <h4>Минусы</h4>
+                <ul class="cons">
+                    {#each result.cons as con}<li>{con}</li>{/each}
+                </ul>
+            </div>
         </div>
+
+        {#if result.warning}
+            <div class="warning">
+                <strong>Внимание:</strong>
+                {result.warning}
+            </div>
+        {/if}
+    {:else}
+        <h2>Не удалось найти подходящий вариант.</h2>
+        <p>
+            Скорее всего, один или несколько выбранных вами вариантов невозможны
+            друг с другом.
+        </p>
     {/if}
 
     <button onclick={reset} class="reset">Начать заново</button>
@@ -47,25 +53,121 @@
 
 <style>
     .result-card {
-        background: #f9f9f9;
+        background: #f8fafc;
         padding: 1.5rem;
         border-radius: 12px;
+        color: #1e293b;
     }
+
+    h2 {
+        color: #0f172a;
+        margin-bottom: 1rem;
+    }
+
+    h4 {
+        color: #334155;
+        margin-top: 1.5rem;
+        margin-bottom: 0.75rem;
+    }
+
+    p {
+        color: #475569;
+        line-height: 1.6;
+    }
+
     .section {
-        margin: 1rem 0;
+        margin: 1.5rem 0;
+        color: #334155;
+    }
+
+    .section ul {
+        list-style: none;
+        padding: 0;
+    }
+
+    .section li {
+        padding: 0.5rem 0;
+        color: #475569;
+    }
+
+    .section strong {
+        color: #1e293b;
+    }
+
+    .section em {
+        color: #64748b;
     }
+
     .pros-cons {
         display: grid;
         grid-template-columns: 1fr 1fr;
         gap: 1rem;
+        margin: 1.5rem 0;
+    }
+
+    @media (max-width: 600px) {
+        .pros-cons {
+            grid-template-columns: 1fr;
+        }
+    }
+
+    .pros,
+    .cons {
+        list-style: none;
+        padding: 0;
+    }
+
+    .pros li {
+        padding: 0.5rem 0;
+        color: #15803d;
+    }
+
+    .pros li::before {
+        content: "✓ ";
+        color: #16a34a;
+        font-weight: bold;
+        margin-right: 0.5rem;
+    }
+
+    .cons li {
+        padding: 0.5rem 0;
+        color: #b91c1c;
+    }
+
+    .cons li::before {
+        content: "✗ ";
+        color: #dc2626;
+        font-weight: bold;
+        margin-right: 0.5rem;
     }
+
     .warning {
-        background: #fff3cd;
+        background: #fef3c7;
         padding: 1rem;
         border-radius: 8px;
-        margin: 1rem 0;
+        margin: 1.5rem 0;
+        border-left: 4px solid #f59e0b;
+        color: #92400e;
+    }
+
+    .warning strong {
+        color: #78350f;
     }
+
     .reset {
         margin-top: 1.5rem;
+        width: 100%;
+        background: #2563eb;
+        color: white;
+        border: none;
+        padding: 0.75rem 1.5rem;
+        border-radius: 8px;
+        font-weight: 600;
+        cursor: pointer;
+        transition: background 0.2s;
+    }
+
+    .reset:hover {
+        background: #1d4ed8;
     }
 </style>

+ 77 - 10
src/lib/components/Step1_Founders.svelte

@@ -4,38 +4,105 @@
 
 <div class="step">
     <h3>1. Кто запускает бизнес?</h3>
+    <p class="hint">Количество учредителей влияет на выбор формы</p>
+    
     <div class="options">
-        <label>
+        <label class="option-card" class:selected={value === 1}>
             <input type="radio" bind:group={value} value={1} />
-            <span>1 человек</span>
+            <div class="content">
+                <strong>1 человек</strong>
+            </div>
         </label>
-        <label>
+        
+        <label class="option-card" class:selected={value === 2}>
             <input type="radio" bind:group={value} value={2} />
-            <span>2–50 человек</span>
+            <div class="content">
+                <strong>2–5 человек</strong>
+            </div>
         </label>
-        <label>
+        
+        <label class="option-card" class:selected={value === 10}>
+            <input type="radio" bind:group={value} value={10} />
+            <div class="content">
+                <strong>6–50 человек</strong>
+            </div>
+        </label>
+        
+        <label class="option-card" class:selected={value === 51}>
             <input type="radio" bind:group={value} value={51} />
-            <span>Больше 50 человек</span>
+            <div class="content">
+                <strong>Больше 50 человек</strong>
+            </div>
         </label>
     </div>
-    <button onclick={next} disabled={!value}>Далее →</button>
+    
+    <button onclick={next} disabled={!value} class="primary">Далее →</button>
 </div>
 
 <style>
+    .hint {
+        color: #666;
+        font-size: 0.9rem;
+        margin-bottom: 1rem;
+    }
     .options {
         display: flex;
         flex-direction: column;
         gap: 1rem;
         margin: 1.5rem 0;
     }
-    label {
+    .option-card {
         display: flex;
         align-items: center;
-        gap: 0.5rem;
+        gap: 1rem;
+        padding: 1rem;
+        border: 2px solid #e2e8f0;
+        border-radius: 12px;
+        cursor: pointer;
+        transition: all 0.2s;
+        background: white;
+    }
+    .option-card:hover {
+        border-color: #2563eb;
+        transform: translateY(-2px);
+    }
+    .option-card.selected {
+        border-color: #2563eb;
+        background: #ebf3ff;
+    }
+    .option-card input {
+        margin: 0;
+    }
+    .content {
+        flex: 1;
+    }
+    .content strong {
+        display: block;
+        margin-bottom: 0.25rem;
         font-size: 1.1rem;
+        color: #1e293b;
+    }
+    .content small {
+        color: #64748b;
+        font-size: 0.85rem;
     }
     button {
+        width: 100%;
         margin-top: 1rem;
         padding: 0.75rem 1.5rem;
+        background: #2563eb;
+        color: white;
+        border: none;
+        border-radius: 8px;
+        font-weight: 600;
+        cursor: pointer;
+        transition: background 0.2s;
+    }
+    button:hover:not(:disabled) {
+        background: #1d4ed8;
+    }
+    button:disabled {
+        background: #cbd5e1;
+        cursor: not-allowed;
     }
-</style>
+</style>

+ 40 - 6
src/lib/components/Step2_Scale.svelte

@@ -34,13 +34,22 @@
 
     <div class="actions">
         <button onclick={prev} class="secondary">Назад</button>
-        <button onclick={next} disabled={!value}>Далее</button>
+        <button onclick={next} disabled={!value} class="primary">Далее</button>
     </div>
 </div>
 
 <style>
+    .step {
+        color: #ffffff;
+    }
+    
+    h3 {
+        color: #ffffff;
+        margin-bottom: 0.5rem;
+    }
+    
     .hint {
-        color: #666;
+        color: #64748b;
         font-size: 0.9rem;
         margin-bottom: 1rem;
     }
@@ -55,13 +64,15 @@
         align-items: center;
         gap: 1rem;
         padding: 1rem;
-        border: 2px solid #ddd;
+        border: 2px solid #e2e8f0;
         border-radius: 12px;
         cursor: pointer;
         transition: all 0.2s;
+        background: white;
     }
     .card:hover {
         border-color: #2563eb;
+        box-shadow: 0 2px 4px rgba(37, 99, 235, 0.1);
     }
     .card.selected {
         border-color: #2563eb;
@@ -70,8 +81,13 @@
     .content {
         flex: 1;
     }
+    .content strong {
+        color: #1e293b;
+        display: block;
+        margin-bottom: 0.25rem;
+    }
     small {
-        color: #555;
+        color: #64748b;
         display: block;
         margin-top: 0.25rem;
     }
@@ -83,9 +99,27 @@
     button {
         padding: 0.75rem 1.5rem;
         border-radius: 8px;
+        border: none;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.2s;
     }
     .secondary {
         background: #f1f5f9;
-        color: #333;
+        color: #475569;
+    }
+    .secondary:hover {
+        background: #e2e8f0;
+    }
+    .primary {
+        background: #2563eb;
+        color: white;
+    }
+    .primary:hover:not(:disabled) {
+        background: #1d4ed8;
+    }
+    .primary:disabled {
+        background: #cbd5e1;
+        cursor: not-allowed;
     }
-</style>
+</style>

+ 51 - 9
src/lib/components/Step3_Activity.svelte

@@ -10,18 +10,32 @@
             id: "услуги",
             title: "Услуги",
             examples: "Консультации, ремонт, обучение",
+            icon: "💼"
         },
         {
             id: "торговля",
             title: "Торговля",
             examples: "Магазин, маркетплейс, опт",
+            icon: "🛒"
         },
         {
             id: "производство",
             title: "Производство",
             examples: "Еда, мебель, оборудование",
+            icon: "🏭"
         },
-        { id: "IT", title: "IT / Стартап", examples: "ПО, приложения, SaaS" },
+        { 
+            id: "IT", 
+            title: "IT / Стартап", 
+            examples: "ПО, приложения, SaaS",
+            icon: "💻"
+        },
+        {
+            id: "сельское хозяйство",
+            title: "Сельское хозяйство",
+            examples: "Ферма, растениеводство, животноводство",
+            icon: "🌾"
+        }
     ];
 </script>
 
@@ -33,7 +47,7 @@
         {#each activities as act}
             <label class="tile" class:selected={value === act.id}>
                 <input type="radio" bind:group={value} value={act.id} />
-                <div class="icon">Icon</div>
+                <div class="icon">{act.icon}</div>
                 <strong>{act.title}</strong>
                 <small>{act.examples}</small>
             </label>
@@ -42,7 +56,7 @@
 
     <div class="actions">
         <button onclick={prev} class="secondary">Назад</button>
-        <button onclick={next} disabled={!value}>Далее</button>
+        <button onclick={next} disabled={!value} class="primary">Далее</button>
     </div>
 </div>
 
@@ -70,21 +84,31 @@
         cursor: pointer;
         transition: all 0.2s;
         background: white;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        color: #1e293b;
+    }
+    .tile input {
+        position: absolute;
+        opacity: 0;
     }
     .tile:hover {
         border-color: #2563eb;
         transform: translateY(-2px);
+        box-shadow: 0 4px 6px rgba(37, 99, 235, 0.1);
     }
     .tile.selected {
         border-color: #2563eb;
         background: #ebf3ff;
     }
     .icon {
-        width: 40px;
-        height: 40px;
-        background: #cbd5e1;
-        border-radius: 50%;
-        margin: 0 auto 0.75rem;
+        font-size: 2.5rem;
+        margin-bottom: 0.75rem;
+    }
+    strong {
+        display: block;
+        margin-bottom: 0.25rem;
     }
     small {
         color: #64748b;
@@ -100,9 +124,27 @@
     button {
         padding: 0.75rem 1.5rem;
         border-radius: 8px;
+        border: none;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.2s;
     }
     .secondary {
         background: #f8fafc;
         color: #334155;
     }
-</style>
+    .secondary:hover {
+        background: #e2e8f0;
+    }
+    .primary {
+        background: #2563eb;
+        color: white;
+    }
+    .primary:hover:not(:disabled) {
+        background: #1d4ed8;
+    }
+    .primary:disabled {
+        background: #cbd5e1;
+        cursor: not-allowed;
+    }
+</style>

+ 37 - 4
src/lib/components/Step4_Finance.svelte

@@ -48,8 +48,17 @@
 </div>
 
 <style>
+    .step {
+        color: #1e293b;
+    }
+    
+    h3 {
+        color: #ffffff;
+        margin-bottom: 0.5rem;
+    }
+    
     .hint {
-        color: #666;
+        color: #64748b;
         margin-bottom: 1.5rem;
     }
     .checkboxes {
@@ -67,13 +76,20 @@
         border-radius: 12px;
         background: #f8fafc;
         cursor: pointer;
+        transition: all 0.2s;
+    }
+    .checkbox-item:hover {
+        border-color: #cbd5e1;
+        background: white;
     }
     .checkbox-item input {
         margin-top: 0.25rem;
+        cursor: pointer;
     }
     .label-text strong {
         display: block;
         margin-bottom: 0.25rem;
+        color: #1e293b;
     }
     .label-text small {
         color: #64748b;
@@ -85,6 +101,10 @@
         border-radius: 8px;
         margin: 1.5rem 0;
         font-size: 0.95rem;
+        border-left: 4px solid #f59e0b;
+    }
+    .warning-box strong {
+        color: #78350f;
     }
     .actions {
         display: flex;
@@ -94,14 +114,27 @@
     button {
         padding: 0.75rem 1.5rem;
         border-radius: 8px;
+        border: none;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.2s;
     }
     .secondary {
         background: #e2e8f0;
-        color: #1e293b;
+        color: #475569;
+    }
+    .secondary:hover {
+        background: #cbd5e1;
     }
     .primary {
         background: #2563eb;
         color: white;
-        font-weight: 600;
     }
-</style>
+    .primary:hover:not(:disabled) {
+        background: #1d4ed8;
+    }
+    .primary:disabled {
+        background: #cbd5e1;
+        cursor: not-allowed;
+    }
+</style>

+ 38 - 12
src/lib/components/Wizard.svelte

@@ -1,6 +1,6 @@
 <!-- Wizard.svelte -->
 <script>
-    import { fade, slide } from "svelte/transition";
+    import { slide } from "svelte/transition";
     import ProgressBar from "./ProgressBar.svelte";
     import Step1_Founders from "./Step1_Founders.svelte";
     import Step2_Scale from "./Step2_Scale.svelte";
@@ -10,8 +10,6 @@
     import { findRecommendation } from "../utils/matcher";
     import rules from "../data/rules.json";
 
-    // TODO: товарищества?, расширить линейку
-
     let step = $state(1);
     let form = $state({
         founders: 1,
@@ -58,20 +56,30 @@
     ];
 </script>
 
-<div class="wizard container" transition:slide>
+<div class="wizard container">
     <h1 class="title">Выбор ОПФ для бизнеса</h1>
     <ProgressBar {step} total={5} />
 
     {#if step === 1}
-        <Step1_Founders bind:value={form.founders} {next} />
+        <div transition:slide>
+            <Step1_Founders bind:value={form.founders} {next} />
+        </div>
     {:else if step === 2}
-        <Step2_Scale bind:value={form.scale} {next} {prev} />
+        <div transition:slide>
+            <Step2_Scale bind:value={form.scale} {next} {prev} />
+        </div>
     {:else if step === 3}
-        <Step3_Activity bind:value={form.activity} {next} {prev} />
+        <div transition:slide>
+            <Step3_Activity bind:value={form.activity} {next} {prev} />
+        </div>
     {:else if step === 4}
-        <Step4_Finance bind:form {next} {prev} />
-    {:else if step === 5 && result}
-        <ResultCard {result} {reset} />
+        <div transition:slide>
+            <Step4_Finance bind:form {next} {prev} />
+        </div>
+    {:else if step === 5}
+        <div transition:slide>
+            <ResultCard {result} {reset} />
+        </div>
     {/if}
 </div>
 
@@ -80,11 +88,29 @@
         max-width: 600px;
         margin: 2rem auto;
         padding: 2rem;
-        background-color: #242627ff;
-        border-radius: 5%;
+        background-color: #ffffff;
+        color: #1e293b;
+        border-radius: 16px;
+        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+        height: min-content;
     }
+
     .title {
         text-align: center;
         margin-bottom: 1rem;
+        color: #0f172a;
+    }
+
+    /* Тёмная тема */
+    @media (prefers-color-scheme: dark) {
+        .wizard {
+            background-color: #1e293b;
+            color: #e2e8f0;
+            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
+        }
+
+        .title {
+            color: #f1f5f9;
+        }
     }
 </style>

+ 81 - 31
src/lib/data/rules.json

@@ -4,11 +4,7 @@
         "conditions": {
             "founders": 1,
             "scale": "малый",
-            "activity": [
-                "услуги",
-                "торговля",
-                "IT"
-            ],
+            "activity": ["услуги", "торговля", "IT"],
             "investments": false,
             "risk": true
         },
@@ -44,16 +40,84 @@
         ],
         "warning": "При долгах — рискуете квартирой, машиной, дачей. Жильё защищено только если оно единственное."
     },
+    {
+        "id": 10,
+        "conditions": {
+            "activity": "сельское хозяйство",
+            "scale": "малый",
+            "founders": {"min": 1, "max": 5},
+            "risk": true,
+            "investments": false
+        },
+        "recommendation": "КФХ (Крестьянское фермерское хозяйство)",
+        "description": "Специальная форма для сельскохозяйственной деятельности. Подходит для фермеров и семейного сельхозбизнеса.",
+        "taxes": [
+            {
+                "name": "ЕСХН",
+                "rate": "6% от прибыли",
+                "note": "Специальный сельхоз режим"
+            },
+            {
+                "name": "УСН",
+                "rate": "6% или 15%",
+                "note": "Альтернативный вариант"
+            }
+        ],
+        "pros": [
+            "Льготные налоговые режимы (ЕСХН)",
+            "Государственная поддержка и субсидии",
+            "Проще регистрация чем у юрлиц",
+            "Семейный бизнес с особым статусом",
+            "Не нужен уставной капитал"
+        ],
+        "cons": [
+            "Только для сельхоздеятельности",
+            "Ограничения по видам деятельности",
+            "Сезонность бизнеса",
+            "Зависимость от природных условий"
+        ],
+        "warning": "Минимум 70% дохода должно поступать от сельскохозяйственной деятельности. Все члены КФХ несут солидарную ответственность."
+    },
+    {
+        "id": 9,
+        "conditions": {
+            "founders": {"min": 2, "max": 50},
+            "activity": ["производство", "торговля", "услуги"],
+            "scale": ["малый", "средний"],
+            "investments": false,
+            "risk": true
+        },
+        "recommendation": "Полное товарищество",
+        "description": "Объединение предпринимателей для совместной деятельности. Подходит для профессиональных партнерств (адвокаты, консультанты).",
+        "taxes": [
+            {
+                "name": "УСН",
+                "rate": "6% или 15%",
+                "note": "Каждый участник платит налоги со своей доли"
+            }
+        ],
+        "pros": [
+            "Объединение ресурсов и компетенций",
+            "Гибкое распределение прибыли",
+            "Проще управление чем в ООО",
+            "Минимальные формальности при создании",
+            "Не нужен уставной капитал"
+        ],
+        "cons": [
+            "Солидарная ответственность по долгам",
+            "Нельзя привлечь инвесторов",
+            "Ограниченные возможности роста",
+            "Сложности при выходе участников",
+            "Все участники отвечают всем имуществом"
+        ],
+        "warning": "ОПАСНО: Все участники несут солидарную ответственность — при долгах товарищества отвечаете всем личным имуществом, даже если долг сделал партнер!"
+    },
     {
         "id": 2,
         "conditions": {
             "founders": 1,
             "scale": "малый",
-            "activity": [
-                "услуги",
-                "торговля",
-                "IT"
-            ],
+            "activity": ["услуги", "торговля", "IT"],
             "investments": false,
             "risk": false
         },
@@ -86,15 +150,10 @@
     {
         "id": 3,
         "conditions": {
-            "founders": {
-                "min": 2,
-                "max": 50
-            },
+            "founders": {"min": 2, "max": 50},
             "investments": false,
-            "scale": [
-                "малый",
-                "средний"
-            ]
+            "risk": false,
+            "scale": ["малый", "средний"]
         },
         "recommendation": "ООО",
         "description": "Классика для партнёрства. Чёткое распределение долей, защита имущества.",
@@ -156,14 +215,9 @@
     {
         "id": 5,
         "conditions": {
-            "founders": {
-                "min": 51
-            },
+            "founders": {"min": 51},
             "scale": "крупный",
-            "activity": [
-                "производство",
-                "торговля"
-            ]
+            "activity": ["производство", "торговля"]
         },
         "recommendation": "АО (НАО или ПАО)",
         "description": "Для крупного бизнеса с акциями. Можно выйти на биржу.",
@@ -191,10 +245,7 @@
         "conditions": {
             "activity": "IT",
             "investments": true,
-            "scale": [
-                "малый",
-                "средний"
-            ]
+            "scale": ["малый", "средний"]
         },
         "recommendation": "ООО + опционы",
         "description": "Стандарт для IT-стартапов. Можно мотивировать команду опционами.",
@@ -254,8 +305,7 @@
             "scale": "малый",
             "activity": "услуги",
             "investments": false,
-            "risk": true,
-            "income_limit": true
+            "risk": true
         },
         "recommendation": "Самозанятый (НПД)",
         "description": "Самая простая форма. Без отчётов, без взносов.",