Skip to content

Commit 86f9c58

Browse files
Copilotnijel
andcommitted
Reorganize charts, add pie chart legend, show all days in daily chart, improve navigation
Co-authored-by: nijel <212189+nijel@users.noreply.github.com>
1 parent b56788d commit 86f9c58

File tree

2 files changed

+73
-48
lines changed

2 files changed

+73
-48
lines changed

weblate_web/crm/templates/crm/income.html

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
{% endif %}
1919

2020
<div class="module">
21-
<h2>
22-
{% translate "Year:" %}
23-
{% for year in years %}
24-
{% if year == current_year %}
25-
<strong>{{ year }}</strong>
26-
{% else %}
27-
<a href="{% url 'crm:income-year' year %}">{{ year }}</a>
28-
{% endif %}
29-
{% if not forloop.last %}|{% endif %}
30-
{% endfor %}
31-
</h2>
21+
<table>
22+
<caption>{% translate "Year" %}</caption>
23+
<tr>
24+
{% for year in years %}
25+
<td>
26+
{% if year == current_year %}
27+
<strong>{{ year }}</strong>
28+
{% else %}
29+
<a href="{% url 'crm:income-year' year %}">{{ year }}</a>
30+
{% endif %}
31+
</td>
32+
{% endfor %}
33+
</tr>
34+
</table>
3235
</div>
3336

3437
<div class="module">
@@ -47,27 +50,22 @@ <h2>
4750
<div class="module">
4851
<h3>
4952
{% if is_monthly %}
50-
{% translate "Income Distribution by Category" %}
53+
{% translate "Daily Income" %}
5154
{% else %}
5255
{% translate "Monthly Income with Category Breakdown" %}
5356
{% endif %}
5457
</h3>
55-
<div class="dashboard-chart">{{ chart_svg|safe }}</div>
58+
{% if is_monthly %}
59+
<div class="dashboard-chart">{{ daily_chart_svg|safe }}</div>
60+
{% else %}
61+
<div class="dashboard-chart">{{ chart_svg|safe }}</div>
62+
{% endif %}
5663
</div>
5764

58-
{% if not is_monthly and pie_chart_svg %}
59-
<div class="module">
60-
<h3>{% translate "Category Distribution" %}</h3>
61-
<div class="dashboard-chart">{{ pie_chart_svg|safe }}</div>
62-
</div>
63-
{% endif %}
64-
65-
{% if is_monthly and daily_chart_svg %}
66-
<div class="module">
67-
<h3>{% translate "Daily Income" %}</h3>
68-
<div class="dashboard-chart">{{ daily_chart_svg|safe }}</div>
69-
</div>
70-
{% endif %}
65+
<div class="module">
66+
<h3>{% translate "Category Distribution" %}</h3>
67+
<div class="dashboard-chart">{{ pie_chart_svg|safe }}</div>
68+
</div>
7169

7270
{% if is_monthly %}
7371
<div class="module">
@@ -76,19 +74,19 @@ <h3>{% translate "Daily Income" %}</h3>
7674
<thead>
7775
<tr>
7876
<th>{% translate "Category" %}</th>
79-
<th class="align-right">{% translate "Amount (EUR)" %}</th>
77+
<th class="align-right nowrap">{% translate "Amount (EUR)" %}</th>
8078
</tr>
8179
</thead>
8280
<tbody>
8381
{% for category, amount in income_data.items %}
8482
<tr>
8583
<td>{{ category }}</td>
86-
<td class="align-right">€{{ amount|floatformat:0 }}</td>
84+
<td class="align-right nowrap">€{{ amount|floatformat:0 }}</td>
8785
</tr>
8886
{% endfor %}
8987
<tr class="total">
9088
<td>{% translate "Total" %}</td>
91-
<td class="align-right">€{{ total_income|floatformat:0 }}</td>
89+
<td class="align-right nowrap">€{{ total_income|floatformat:0 }}</td>
9290
</tr>
9391
</tbody>
9492
</table>
@@ -129,14 +127,14 @@ <h3>{% translate "Daily Income" %}</h3>
129127
<thead>
130128
<tr>
131129
<th>{% translate "Category" %}</th>
132-
<th class="align-right">{% translate "Amount (EUR)" %}</th>
130+
<th class="align-right nowrap">{% translate "Amount (EUR)" %}</th>
133131
</tr>
134132
</thead>
135133
<tbody>
136134
{% for category, amount in income_data.items %}
137135
<tr>
138136
<td>{{ category }}</td>
139-
<td class="align-right">€{{ amount|floatformat:0 }}</td>
137+
<td class="align-right nowrap">€{{ amount|floatformat:0 }}</td>
140138
</tr>
141139
{% endfor %}
142140
</tbody>

weblate_web/crm/views.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -427,15 +427,15 @@ def generate_svg_bar_chart( # noqa: PLR0914
427427
return "".join(svg_parts)
428428

429429
def generate_svg_pie_chart(self, data: dict[str, Decimal]) -> str: # noqa: PLR0914
430-
"""Generate a simple SVG pie chart for category distribution."""
430+
"""Generate a simple SVG pie chart for category distribution with legend."""
431431
if not data or sum(data.values()) == 0:
432432
return ""
433433

434-
width = 400
434+
width = 500
435435
height = 400
436-
radius = 150
437-
center_x = width / 2
438-
center_y = height / 2
436+
radius = 120
437+
center_x = 200
438+
center_y = 200
439439

440440
# Category colors
441441
colors = {
@@ -498,6 +498,33 @@ def generate_svg_pie_chart(self, data: dict[str, Decimal]) -> str: # noqa: PLR0
498498

499499
start_angle = end_angle
500500

501+
# Add legend
502+
legend_x = 420
503+
legend_y = 50
504+
legend_spacing = 25
505+
506+
idx = 0
507+
for category, value in data.items():
508+
if value == 0:
509+
continue
510+
511+
color = colors.get(category, "#999")
512+
y_pos = legend_y + idx * legend_spacing
513+
514+
# Legend color box
515+
svg_parts.append(
516+
f'<rect x="{legend_x}" y="{y_pos}" width="15" height="15" '
517+
f'fill="{color}" stroke="white" stroke-width="1"/>'
518+
)
519+
520+
# Legend text
521+
svg_parts.append(
522+
f'<text x="{legend_x + 20}" y="{y_pos + 12}" font-size="11" fill="#333">'
523+
f"{category}: €{value:,.0f} ({value / total * 100:.0f}%)</text>"
524+
)
525+
526+
idx += 1
527+
501528
svg_parts.append("</svg>")
502529
return "".join(svg_parts)
503530

@@ -631,14 +658,13 @@ def generate_svg_daily_chart( # noqa: PLR0914
631658
f"</rect>"
632659
)
633660

634-
# Show day label every 5 days
635-
if day % 5 == 1 or day == num_days:
636-
label_x = x + bar_width / 2
637-
label_y = height - padding + 12
638-
svg_parts.append(
639-
f'<text x="{label_x}" y="{label_y}" text-anchor="middle" '
640-
f'font-size="9" fill="#666">{day}</text>'
641-
)
661+
# Show all day labels
662+
label_x = x + bar_width / 2
663+
label_y = height - padding + 12
664+
svg_parts.append(
665+
f'<text x="{label_x}" y="{label_y}" text-anchor="middle" '
666+
f'font-size="9" fill="#666">{day}</text>'
667+
)
642668

643669
svg_parts.append("</svg>")
644670
return "".join(svg_parts)
@@ -746,8 +772,11 @@ def get_context_data(self, **kwargs):
746772
context["current_month"] = month
747773

748774
# Generate charts
775+
# Always generate pie chart for both views
776+
context["pie_chart_svg"] = self.generate_svg_pie_chart(income_data)
777+
749778
if month:
750-
# For monthly view, show category pie chart and daily chart
779+
# For monthly view, show daily chart
751780
# Get all invoices for the month
752781
month_invoices = list(
753782
Invoice.objects.filter(
@@ -757,18 +786,16 @@ def get_context_data(self, **kwargs):
757786
).prefetch_related("invoiceitem_set")
758787
)
759788

760-
context["chart_svg"] = self.generate_svg_pie_chart(income_data)
761789
context["daily_chart_svg"] = self.generate_svg_daily_chart(
762790
year, month, month_invoices
763791
)
764792
context["is_monthly"] = True
765793
else:
766-
# For yearly view, show stacked monthly chart and category pie
794+
# For yearly view, show stacked monthly chart
767795
monthly_data, invoices = self.get_monthly_data(year)
768796
context["chart_svg"] = self.generate_svg_stacked_bar_chart(
769797
monthly_data, invoices
770798
)
771-
context["pie_chart_svg"] = self.generate_svg_pie_chart(income_data)
772799
context["monthly_data"] = monthly_data
773800
context["monthly_category_data"], _ = self.get_monthly_category_data(year)
774801
context["is_monthly"] = False

0 commit comments

Comments
 (0)