Initial commit: KeepItGoing task management server

Features:
- Django-based REST API with web interface
- Task management with tags, priorities, and due dates
- Time tracking with start/stop timers
- Subtasks support
- Task filtering (all, today, upcoming, overdue, completed)
- Tag-based organization with color coding
- Sorting by due date and priority
- Auto-assign tags when filtering
- Responsive 3-pane layout (sidebar, task list, detail panel)
- Task sharing between users
- Mobile-responsive design with dark mode support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2025-12-18 17:41:29 -07:00
co-authored by Claude Sonnet 4.5
commit 6a0b35c39c
84 changed files with 7763 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
"""
User views for KeepItGoing.
"""
from django.contrib.auth import get_user_model, login, logout
from django.shortcuts import render, redirect
from django.views import View
from rest_framework import generics, permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import (
UserSerializer,
UserRegistrationSerializer,
ChangePasswordSerializer,
DeviceTokenSerializer,
)
from .models import DeviceToken
User = get_user_model()
# =============================================================================
# API Views
# =============================================================================
class RegisterAPIView(generics.CreateAPIView):
"""API endpoint for user registration."""
queryset = User.objects.all()
serializer_class = UserRegistrationSerializer
permission_classes = [permissions.AllowAny]
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
# Generate tokens
refresh = RefreshToken.for_user(user)
return Response({
'user': UserSerializer(user).data,
'tokens': {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
}, status=status.HTTP_201_CREATED)
class UserProfileAPIView(generics.RetrieveUpdateAPIView):
"""API endpoint for user profile."""
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user
class ChangePasswordAPIView(APIView):
"""API endpoint for changing password."""
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
serializer = ChangePasswordSerializer(
data=request.data,
context={'request': request}
)
serializer.is_valid(raise_exception=True)
request.user.set_password(serializer.validated_data['new_password'])
request.user.save()
return Response({'message': 'Password changed successfully.'})
class DeviceTokenAPIView(generics.ListCreateAPIView):
"""API endpoint for managing device tokens."""
serializer_class = DeviceTokenSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return DeviceToken.objects.filter(user=self.request.user)
class DeviceTokenDeleteAPIView(generics.DestroyAPIView):
"""API endpoint for deleting a device token."""
serializer_class = DeviceTokenSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return DeviceToken.objects.filter(user=self.request.user)
# =============================================================================
# Web Views (Django Templates)
# =============================================================================
class LoginView(View):
"""Web login page."""
def get(self, request):
if request.user.is_authenticated:
return redirect('dashboard')
return render(request, 'users/login.html')
def post(self, request):
from django.contrib.auth import authenticate
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(request, username=email, password=password)
if user is not None:
login(request, user)
next_url = request.GET.get('next', 'dashboard')
return redirect(next_url)
return render(request, 'users/login.html', {
'error': 'Invalid email or password.'
})
class LogoutView(View):
"""Web logout."""
def get(self, request):
logout(request)
return redirect('login')
def post(self, request):
logout(request)
return redirect('login')
class RegisterView(View):
"""Web registration page."""
def get(self, request):
if request.user.is_authenticated:
return redirect('dashboard')
return render(request, 'users/register.html')
def post(self, request):
email = request.POST.get('email')
username = request.POST.get('username')
password = request.POST.get('password')
password_confirm = request.POST.get('password_confirm')
errors = {}
if password != password_confirm:
errors['password_confirm'] = 'Passwords do not match.'
if User.objects.filter(email=email).exists():
errors['email'] = 'Email already registered.'
if User.objects.filter(username=username).exists():
errors['username'] = 'Username already taken.'
if errors:
return render(request, 'users/register.html', {'errors': errors})
user = User.objects.create_user(
email=email,
username=username,
password=password
)
login(request, user)
return redirect('dashboard')
class ProfileView(View):
"""Web profile page."""
def get(self, request):
if not request.user.is_authenticated:
return redirect('login')
return render(request, 'users/profile.html')
def post(self, request):
if not request.user.is_authenticated:
return redirect('login')
user = request.user
user.first_name = request.POST.get('first_name', '')
user.last_name = request.POST.get('last_name', '')
user.timezone = request.POST.get('timezone', 'UTC')
user.default_reminder_minutes = int(request.POST.get('default_reminder_minutes', 30))
user.email_notifications = request.POST.get('email_notifications') == 'on'
user.push_notifications = request.POST.get('push_notifications') == 'on'
user.save()
return render(request, 'users/profile.html', {
'success': 'Profile updated successfully.'
})