FossilRepo

fossilrepo / templates / accounts / profile.html
1
{% extends "base.html" %}
2
{% block title %}Profile — Fossilrepo{% endblock %}
3
4
{% block content %}
5
<!-- Profile Header -->
6
<div class="sm:flex sm:items-center gap-5 mb-8">
7
<div class="flex items-center gap-4 sm:gap-5 flex-1 min-w-0">
8
<div class="flex-shrink-0 h-16 w-16 rounded-full bg-brand flex items-center justify-center text-white text-2xl font-bold">
9
{{ user.username|make_list|first|upper }}
10
</div>
11
<div class="min-w-0">
12
<h1 class="text-2xl font-bold text-gray-100 truncate">
13
{% if user.get_full_name %}{{ user.get_full_name }}{% else %}{{ user.username }}{% endif %}
14
</h1>
15
<div class="flex items-center gap-3 mt-1 text-sm text-gray-400">
16
{% if user_profile.handle %}
17
<span>@{{ user_profile.handle }}</span>
18
{% endif %}
19
<span>{{ user.email }}</span>
20
</div>
21
</div>
22
</div>
23
<div class="flex flex-wrap items-center gap-2 mt-4 sm:mt-0">
24
<a href="{% url 'accounts:profile_edit' %}"
25
class="rounded-md bg-gray-700 px-4 py-2 text-sm font-semibold text-gray-200 hover:bg-gray-600 border border-gray-600 transition-colors">
26
Edit Profile
27
</a>
28
<a href="{% url 'organization:user_password' username=user.username %}"
29
class="rounded-md bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:text-gray-200 hover:bg-gray-700 ring-1 ring-gray-700 transition-colors">
30
Change Password
31
</a>
32
</div>
33
</div>
34
35
<!-- Profile Info Card -->
36
<div class="rounded-lg bg-gray-800 border border-gray-700 p-6 mb-6 shadow-sm">
37
<h2 class="text-lg font-semibold text-gray-200 mb-4">Profile Info</h2>
38
<dl class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm">
39
<div>
40
<dt class="text-gray-500">Full Name</dt>
41
<dd class="text-gray-200 mt-0.5">{{ user.get_full_name|default:"Not set" }}</dd>
42
</div>
43
<div>
44
<dt class="text-gray-500">Handle</dt>
45
<dd class="text-gray-200 mt-0.5">{% if user_profile.handle %}@{{ user_profile.handle }}{% else %}Not set{% endif %}</dd>
46
</div>
47
<div>
48
<dt class="text-gray-500">Email</dt>
49
<dd class="text-gray-200 mt-0.5">{{ user.email|default:"Not set" }}</dd>
50
</div>
51
<div>
52
<dt class="text-gray-500">Location</dt>
53
<dd class="text-gray-200 mt-0.5">{{ user_profile.location|default:"Not set" }}</dd>
54
</div>
55
<div>
56
<dt class="text-gray-500">Website</dt>
57
<dd class="text-gray-200 mt-0.5">
58
{% if user_profile.website %}
59
<a href="{{ user_profile.website }}" class="text-brand-light hover:text-brand" target="_blank" rel="noopener">{{ user_profile.website }}</a>
60
{% else %}Not set{% endif %}
61
</dd>
62
</div>
63
<div class="sm:col-span-2">
64
<dt class="text-gray-500">Bio</dt>
65
<dd class="text-gray-200 mt-0.5">{{ user_profile.bio|default:"Not set"|linebreaksbr }}</dd>
66
</div>
67
</dl>
68
</div>
69
70
<!-- SSH Keys Card -->
71
<div class="rounded-lg bg-gray-800 border border-gray-700 mb-6 shadow-sm">
72
<div class="p-4 border-b border-gray-700 flex items-center justify-between">
73
<h2 class="text-lg font-semibold text-gray-200">SSH Keys</h2>
74
<a href="{% url 'accounts:ssh_keys' %}" class="text-sm text-brand-light hover:text-brand">Manage Keys</a>
75
</div>
76
{% if ssh_keys %}
77
<div class="divide-y divide-gray-700">
78
{% for key in ssh_keys %}
79
<div class="p-4 flex flex-wrap items-center justify-between gap-3">
80
<div class="min-w-0">
81
<div class="text-sm font-medium text-gray-200">{{ key.title }}</div>
82
<div class="text-xs text-gray-500 font-mono mt-1 truncate">{{ key.fingerprint }}</div>
83
<div class="text-xs text-gray-500 mt-1">
84
{{ key.key_type|upper }} &middot; Added {{ key.created_at|timesince }} ago
85
{% if key.last_used_at %}&middot; Last used {{ key.last_used_at|timesince }} ago{% endif %}
86
</div>
87
</div>
88
</div>
89
{% endfor %}
90
</div>
91
{% else %}
92
<div class="p-6 text-center text-sm text-gray-500">
93
No SSH keys added yet. <a href="{% url 'accounts:ssh_keys' %}" class="text-brand-light hover:text-brand">Add one</a> to clone and push over SSH.
94
</div>
95
{% endif %}
96
</div>
97
98
<!-- Notification Preferences Card -->
99
<div class="rounded-lg bg-gray-800 border border-gray-700 mb-6 shadow-sm">
100
<div class="p-4 border-b border-gray-700 flex items-center justify-between">
101
<h2 class="text-lg font-semibold text-gray-200">Notification Preferences</h2>
102
<a href="{% url 'accounts:notification_prefs' %}" class="text-sm text-brand-light hover:text-brand">Edit Preferences</a>
103
</div>
104
<div class="p-4">
105
<dl class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm">
106
<div>
107
<dt class="text-gray-500">Delivery Mode</dt>
108
<dd class="text-gray-200 mt-0.5 capitalize">{{ notif_prefs.delivery_mode }}</dd>
109
</div>
110
<div>
111
<dt class="text-gray-500">Events</dt>
112
<dd class="text-gray-200 mt-0.5">
113
{% if notif_prefs.notify_checkins %}Checkins{% endif %}
114
{% if notif_prefs.notify_tickets %}{% if notif_prefs.notify_checkins %}, {% endif %}Tickets{% endif %}
115
{% if notif_prefs.notify_wiki %}{% if notif_prefs.notify_checkins or notif_prefs.notify_tickets %}, {% endif %}Wiki{% endif %}
116
{% if notif_prefs.notify_releases %}{% if notif_prefs.notify_checkins or notif_prefs.notify_tickets or notif_prefs.notify_wiki %}, {% endif %}Releases{% endif %}
117
{% if notif_prefs.notify_forum %}{% if notif_prefs.notify_checkins or notif_prefs.notify_tickets or notif_prefs.notify_wiki or notif_prefs.notify_releases %}, {% endif %}Forum{% endif %}
118
{% if not notif_prefs.notify_checkins and not notif_prefs.notify_tickets and not notif_prefs.notify_wiki and not notif_prefs.notify_releases and not notif_prefs.notify_forum %}None{% endif %}
119
</dd>
120
</div>
121
</dl>
122
</div>
123
</div>
124
125
<!-- Personal Access Tokens Card -->
126
<div class="rounded-lg bg-gray-800 border border-gray-700 mb-6 shadow-sm">
127
<div class="p-4 border-b border-gray-700 flex items-center justify-between">
128
<h2 class="text-lg font-semibold text-gray-200">Personal Access Tokens</h2>
129
<a href="{% url 'accounts:profile_token_create' %}"
130
class="rounded-md bg-brand px-3 py-1.5 text-sm font-semibold text-white hover:bg-brand-hover focus:outline-none focus:ring-2 focus:ring-brand focus:ring-offset-2 focus:ring-offset-gray-950 transition-colors">
131
Generate Token
132
</a>
133
</div>
134
{% if tokens %}
135
<div class="divide-y divide-gray-700">
136
{% for token in tokens %}
137
<div class="p-4 flex flex-wrap items-center justify-between gap-3">
138
<div class="min-w-0">
139
<div class="text-sm font-medium text-gray-200">{{ token.name }}</div>
140
<div class="text-xs text-gray-500 font-mono mt-1 truncate">{{ token.token_prefix }}...</div>
141
<div class="text-xs text-gray-500 mt-1 break-words">
142
Scopes: {{ token.scopes }} &middot; Created {{ token.created_at|timesince }} ago
143
{% if token.last_used_at %}&middot; Last used {{ token.last_used_at|timesince }} ago{% endif %}
144
{% if token.expires_at %}&middot; Expires {{ token.expires_at|date:"M j, Y" }}{% endif %}
145
</div>
146
</div>
147
<form hx-post="{% url 'accounts:profile_token_revoke' guid=token.token_prefix %}"
148
hx-confirm="Revoke token '{{ token.name }}'? This cannot be undone.">
149
{% csrf_token %}
150
<button type="submit" class="text-sm text-red-400 hover:text-red-300">Revoke</button>
151
</form>
152
</div>
153
{% endfor %}
154
</div>
155
{% else %}
156
<div class="p-6 text-center text-sm text-gray-500">
157
No personal access tokens. Generate one to authenticate with the API or CLI.
158
</div>
159
{% endif %}
160
</div>
161
{% endblock %}
162

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button